2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* EFhier.c -
|
|
|
|
|
*
|
|
|
|
|
* Procedures for manipulating HierNames.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
|
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
|
|
|
* * software and its documentation for any purpose and without *
|
|
|
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
|
|
|
* * notice appear in all copies. The University of California *
|
|
|
|
|
* * makes no representations about the suitability of this *
|
|
|
|
|
* * software for any purpose. It is provided "as is" without *
|
|
|
|
|
* * express or implied warranty. Export of this software outside *
|
|
|
|
|
* * of the United States of America may require an export license. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/extflat/EFname.c,v 1.2 2010/08/10 00:18:45 tim Exp $";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "utils/geofast.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "utils/utils.h"
|
|
|
|
|
#include "extflat/extflat.h"
|
|
|
|
|
#include "extflat/EFint.h"
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2017-04-25 14:41:48 +02:00
|
|
|
#define PrintErr TxError
|
|
|
|
|
#else
|
|
|
|
|
#define PrintErr printf
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Hash table containing all flattened node names.
|
|
|
|
|
* The keys in this table are HierNames, processed by the
|
|
|
|
|
* procedures efHNCompare(), efHNHash(), efHierCopy(),
|
|
|
|
|
* and efHierKill().
|
|
|
|
|
*/
|
|
|
|
|
HashTable efNodeHashTable;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Hash table used by efHNFromUse to ensure that it always returns
|
|
|
|
|
* a pointer to the SAME HierName structure each time it is called
|
|
|
|
|
* with the same fields.
|
|
|
|
|
*/
|
|
|
|
|
HashTable efHNUseHashTable;
|
|
|
|
|
|
|
|
|
|
extern void EFHNFree();
|
|
|
|
|
extern void efHNInit();
|
|
|
|
|
extern void efHNRecord();
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFHNIsGlob --
|
|
|
|
|
*
|
|
|
|
|
* Determine whether a HierName is of the format of a global name,
|
|
|
|
|
* i.e, it ends in a '!'.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* The Tcl version of magic further refines this to include names
|
|
|
|
|
* which are defined in the global Tcl variable space. (7.3.94):
|
|
|
|
|
* also check if the array variable "globals" contains the name as
|
|
|
|
|
* a key entry.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if the name is a global.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
EFHNIsGlob(hierName)
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
char *retstr;
|
|
|
|
|
retstr = (char *)Tcl_GetVar2(magicinterp, "globals", hierName->hn_name,
|
|
|
|
|
TCL_GLOBAL_ONLY);
|
|
|
|
|
if (retstr != NULL) return TRUE;
|
|
|
|
|
|
|
|
|
|
retstr = (char *)Tcl_GetVar(magicinterp, hierName->hn_name, TCL_GLOBAL_ONLY);
|
|
|
|
|
if (retstr != NULL) return TRUE;
|
|
|
|
|
#endif
|
|
|
|
|
return hierName->hn_name[strlen(hierName->hn_name) - 1] == '!';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFHNIsGND --
|
|
|
|
|
*
|
|
|
|
|
* Determine whether a HierName is the same as the global signal GND.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* The Tcl version of magic expands this to include names which are
|
|
|
|
|
* equal to the global Tcl variable $GND, if it is set.
|
|
|
|
|
*
|
|
|
|
|
* This is only used in substrate backwards-compatibility mode, when the
|
|
|
|
|
* substrate is not specified in the technology file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if the name is GND, false if not.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
EFHNIsGND(hierName)
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
char *retstr;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (hierName->hn_parent != (HierName *)NULL) return FALSE;
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
retstr = (char *)Tcl_GetVar(magicinterp, "GND", TCL_GLOBAL_ONLY);
|
|
|
|
|
if (retstr != NULL)
|
|
|
|
|
if (!strcmp(hierName->hn_name, retstr)) return TRUE;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return (strcmp(hierName->hn_name, "GND!") == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFHNConcat --
|
|
|
|
|
*
|
|
|
|
|
* Given a HierName prefix and a HierName suffix, make a newly allocated
|
|
|
|
|
* copy of the suffix that points to the prefix.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Pointer to the new HierName as described above.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* May allocate memory.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
HierName *
|
|
|
|
|
EFHNConcat(prefix, suffix)
|
|
|
|
|
HierName *prefix; /* Components of name on root side */
|
|
|
|
|
HierName *suffix; /* Components of name on leaf side */
|
|
|
|
|
{
|
|
|
|
|
HierName *new, *prev;
|
|
|
|
|
HierName *firstNew;
|
|
|
|
|
unsigned size;
|
|
|
|
|
|
|
|
|
|
for (firstNew = prev = (HierName *) NULL;
|
|
|
|
|
suffix;
|
|
|
|
|
prev = new, suffix = suffix->hn_parent)
|
|
|
|
|
{
|
|
|
|
|
size = HIERNAMESIZE(strlen(suffix->hn_name));
|
|
|
|
|
new = (HierName *) mallocMagic((unsigned)(size));
|
|
|
|
|
if (efHNStats) efHNRecord(size, HN_CONCAT);
|
|
|
|
|
new->hn_hash = suffix->hn_hash;
|
|
|
|
|
(void) strcpy(new->hn_name, suffix->hn_name);
|
|
|
|
|
if (prev)
|
|
|
|
|
prev->hn_parent = new;
|
|
|
|
|
else
|
|
|
|
|
firstNew = new;
|
|
|
|
|
}
|
|
|
|
|
prev->hn_parent = prefix;
|
|
|
|
|
|
|
|
|
|
return firstNew;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFStrToHN --
|
|
|
|
|
*
|
|
|
|
|
* Given a hierarchical prefix (the HierName pointed to by prefix)
|
|
|
|
|
* and a name relative to that prefix (the string 'suffixStr'), return a
|
|
|
|
|
* pointer to the HierName we should use. Normally, this is just a newly
|
|
|
|
|
* built HierName containing the path components of 'suffixStr' appended to
|
|
|
|
|
* prefix.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Pointer to a name determined as described above.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* May allocate new HierNames.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
HierName *
|
|
|
|
|
EFStrToHN(prefix, suffixStr)
|
|
|
|
|
HierName *prefix; /* Components of name on side of root */
|
|
|
|
|
char *suffixStr; /* Leaf part of name (may have /'s) */
|
|
|
|
|
{
|
|
|
|
|
char *cp;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
char *slashPtr;
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
unsigned size;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
/* Skip to the end of the relative name */
|
|
|
|
|
slashPtr = NULL;
|
|
|
|
|
for (cp = suffixStr; *cp; cp++)
|
|
|
|
|
if (*cp == '/')
|
|
|
|
|
slashPtr = cp;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Convert the relative name into a HierName path, with one HierName
|
2021-02-24 18:35:06 +01:00
|
|
|
* created for each slash-separated segment of suffixStr.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
cp = slashPtr = suffixStr;
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
if (*cp == '/' || *cp == '\0')
|
|
|
|
|
{
|
|
|
|
|
size = HIERNAMESIZE(cp - slashPtr);
|
|
|
|
|
hierName = (HierName *) mallocMagic((unsigned)(size));
|
|
|
|
|
if (efHNStats) efHNRecord(size, HN_ALLOC);
|
|
|
|
|
efHNInit(hierName, slashPtr, cp);
|
|
|
|
|
hierName->hn_parent = prefix;
|
|
|
|
|
if (*cp++ == '\0')
|
|
|
|
|
break;
|
|
|
|
|
slashPtr = cp;
|
|
|
|
|
prefix = hierName;
|
|
|
|
|
}
|
|
|
|
|
else cp++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hierName;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFHNToStr --
|
|
|
|
|
*
|
|
|
|
|
* Convert a HierName chain into a printable name.
|
|
|
|
|
* Stores the result in a static buffer.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to the static buffer containing the
|
|
|
|
|
* printable name.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Overwrites the previous contents of the static buffer.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
EFHNToStr(hierName)
|
|
|
|
|
HierName *hierName; /* Name to be converted */
|
|
|
|
|
{
|
|
|
|
|
static char namebuf[2048];
|
|
|
|
|
|
|
|
|
|
(void) efHNToStrFunc(hierName, namebuf);
|
|
|
|
|
return namebuf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* efHNToStrFunc --
|
|
|
|
|
*
|
|
|
|
|
* Recursive part of name conversion.
|
|
|
|
|
* Calls itself recursively on hierName->hn_parent and dstp,
|
|
|
|
|
* adding the prefix of the pathname to the string pointed to
|
|
|
|
|
* by dstp. Then stores hierName->hn_name at the end of the
|
|
|
|
|
* just-stored prefix, and returns a pointer to the end.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to the null byte at the end of
|
|
|
|
|
* all the pathname components stored so far in dstp.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Stores characters in dstp.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
efHNToStrFunc(hierName, dstp)
|
|
|
|
|
HierName *hierName; /* Name to be converted */
|
|
|
|
|
char *dstp; /* Store name here */
|
|
|
|
|
{
|
|
|
|
|
char *srcp;
|
|
|
|
|
|
|
|
|
|
if (hierName == NULL)
|
|
|
|
|
{
|
|
|
|
|
*dstp = '\0';
|
|
|
|
|
return dstp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hierName->hn_parent)
|
|
|
|
|
{
|
|
|
|
|
dstp = efHNToStrFunc(hierName->hn_parent, dstp);
|
|
|
|
|
*dstp++ = '/';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srcp = hierName->hn_name;
|
|
|
|
|
while (*dstp++ = *srcp++)
|
|
|
|
|
/* Nothing */;
|
|
|
|
|
|
|
|
|
|
return --dstp;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFHNLook --
|
|
|
|
|
*
|
|
|
|
|
* Look for the entry in the efNodeHashTable whose name is formed
|
|
|
|
|
* by concatenating suffixStr to prefix. If there's not an
|
|
|
|
|
* entry in efNodeHashTable, or the entry has a NULL value, complain
|
|
|
|
|
* and return NULL; otherwise return the HashEntry.
|
|
|
|
|
*
|
|
|
|
|
* The string errorStr should say what we were processing, e.g,
|
|
|
|
|
* "fet", "connect(1)", "connect(2)", etc., for use in printing
|
|
|
|
|
* error messages. If errorStr is NULL, we don't print any error
|
|
|
|
|
* messages.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Allocates memory temporarily to build the key for HashLookOnly(),
|
|
|
|
|
* but then frees it before returning.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
HashEntry *
|
|
|
|
|
EFHNLook(prefix, suffixStr, errorStr)
|
|
|
|
|
HierName *prefix; /* Components of name on root side */
|
|
|
|
|
char *suffixStr; /* Part of name on leaf side */
|
|
|
|
|
char *errorStr; /* Explanatory string for errors */
|
|
|
|
|
{
|
|
|
|
|
HierName *hierName, *hn;
|
|
|
|
|
bool dontFree = FALSE;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
|
|
|
|
if (suffixStr == NULL)
|
|
|
|
|
{
|
|
|
|
|
hierName = prefix;
|
|
|
|
|
dontFree = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else hierName = EFStrToHN(prefix, suffixStr);
|
|
|
|
|
|
|
|
|
|
he = HashLookOnly(&efNodeHashTable, (char *) hierName);
|
|
|
|
|
if (he == NULL || HashGetValue(he) == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (errorStr)
|
|
|
|
|
PrintErr("%s: no such node %s\n", errorStr, EFHNToStr(hierName));
|
|
|
|
|
he = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Free the portion of the HierName we just allocated for
|
|
|
|
|
* looking in the table, if we allocated one.
|
|
|
|
|
*/
|
|
|
|
|
if (!dontFree)
|
|
|
|
|
EFHNFree(hierName, prefix, HN_ALLOC);
|
|
|
|
|
|
|
|
|
|
return he;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFHNConcatLook --
|
|
|
|
|
*
|
|
|
|
|
* Like EFHNLook above, but the argument suffix is itself a HierName.
|
|
|
|
|
* We construct the full name by concatenating the hierarchical prefix
|
|
|
|
|
* and the node name 'suffix', then looking it up in the flat node
|
|
|
|
|
* table for its real name.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* See EFHNLook()'s comments.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See EFHNLook()'s comments.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
HashEntry *
|
|
|
|
|
EFHNConcatLook(prefix, suffix, errorStr)
|
|
|
|
|
HierName *prefix; /* Components of name on root side */
|
|
|
|
|
HierName *suffix; /* Part of name on leaf side */
|
|
|
|
|
char *errorStr; /* Explanatory string for errors */
|
|
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
HierName *hn;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find the last component of the suffix, then temporarily
|
|
|
|
|
* link the HierNames for use as a hash key. This is safe
|
|
|
|
|
* because HashLookOnly() doesn't ever store anything in the
|
|
|
|
|
* hash table, so we don't have to worry about this temporarily
|
|
|
|
|
* built key somehow being saved without our knowledge.
|
|
|
|
|
*/
|
|
|
|
|
hn = suffix;
|
|
|
|
|
while (hn->hn_parent)
|
|
|
|
|
hn = hn->hn_parent;
|
|
|
|
|
hn->hn_parent = prefix;
|
|
|
|
|
|
|
|
|
|
he = HashLookOnly(&efNodeHashTable, (char *) suffix);
|
|
|
|
|
if (he == NULL || HashGetValue(he) == NULL)
|
|
|
|
|
{
|
|
|
|
|
PrintErr("%s: no such node %s\n", errorStr, EFHNToStr(suffix));
|
|
|
|
|
he = (HashEntry *) NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Undo the temp link */
|
|
|
|
|
hn->hn_parent = (HierName *) NULL;
|
|
|
|
|
return he;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFHNFree --
|
|
|
|
|
*
|
|
|
|
|
* Free a list of HierNames, up to but not including the element pointed
|
|
|
|
|
* to by 'prefix' (or the NULL indicating the end of the HierName list).
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Frees memory.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
EFHNFree(hierName, prefix, type)
|
|
|
|
|
HierName *hierName, *prefix;
|
|
|
|
|
int type; /* HN_ALLOC, HN_CONCAT, etc */
|
|
|
|
|
{
|
|
|
|
|
HierName *hn;
|
|
|
|
|
|
|
|
|
|
for (hn = hierName; hn; hn = hn->hn_parent)
|
|
|
|
|
{
|
|
|
|
|
if (hn == prefix)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
freeMagic((char *) hn);
|
|
|
|
|
if (efHNStats)
|
|
|
|
|
{
|
|
|
|
|
int len = strlen(hn->hn_name);
|
|
|
|
|
efHNRecord(-HIERNAMESIZE(len), type);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFHNBest --
|
|
|
|
|
*
|
|
|
|
|
* Determine which of two names is more preferred. The most preferred
|
|
|
|
|
* name is a global name. Given two non-global names, the one with the
|
|
|
|
|
* fewest pathname components is the most preferred. If the two names
|
|
|
|
|
* have equally many pathname components, we choose the shortest.
|
|
|
|
|
* If they both are of the same length, we choose the one that comes
|
|
|
|
|
* later in the alphabet.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if the first name is preferable to the second, FALSE if not.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
EFHNBest(hierName1, hierName2)
|
|
|
|
|
HierName *hierName1, *hierName2;
|
|
|
|
|
{
|
|
|
|
|
int ncomponents1, ncomponents2, len1, len2;
|
|
|
|
|
HierName *np1, *np2;
|
|
|
|
|
char last1, last2;
|
|
|
|
|
|
|
|
|
|
for (ncomponents1 = 0, np1 = hierName1; np1; np1 = np1->hn_parent)
|
|
|
|
|
ncomponents1++;
|
|
|
|
|
for (ncomponents2 = 0, np2 = hierName2; np2; np2 = np2->hn_parent)
|
|
|
|
|
ncomponents2++;
|
|
|
|
|
|
|
|
|
|
last1 = hierName1->hn_name[strlen(hierName1->hn_name) - 1];
|
|
|
|
|
last2 = hierName2->hn_name[strlen(hierName2->hn_name) - 1];
|
|
|
|
|
if (last1 != '!' || last2 != '!')
|
|
|
|
|
{
|
|
|
|
|
/* Prefer global over local names */
|
|
|
|
|
if (last1 == '!') return TRUE;
|
|
|
|
|
if (last2 == '!') return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Neither name is global, so chose label over generated name */
|
|
|
|
|
if (last1 != '#' && last2 == '#') return TRUE;
|
|
|
|
|
if (last1 == '#' && last2 != '#') return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Compare two names the hard way. Both are of the same class,
|
|
|
|
|
* either both global or both non-global, so compare in order:
|
|
|
|
|
* number of pathname components, length, and lexicographic
|
|
|
|
|
* ordering.
|
|
|
|
|
*/
|
|
|
|
|
if (ncomponents1 < ncomponents2) return TRUE;
|
|
|
|
|
if (ncomponents1 > ncomponents2) return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Non-default substrate node name is preferred over "0" */
|
|
|
|
|
if (ncomponents1 == 1 && !strcmp(hierName1->hn_name, "0")) return FALSE;
|
|
|
|
|
if (ncomponents2 == 1 && !strcmp(hierName2->hn_name, "0")) return TRUE;
|
|
|
|
|
|
|
|
|
|
/* Same # of pathname components; check length */
|
|
|
|
|
for (len1 = 0, np1 = hierName1; np1; np1 = np1->hn_parent)
|
|
|
|
|
len1 += strlen(np1->hn_name);
|
|
|
|
|
for (len2 = 0, np2 = hierName2; np2; np2 = np2->hn_parent)
|
|
|
|
|
len2 += strlen(np2->hn_name);
|
|
|
|
|
if (len1 < len2) return TRUE;
|
|
|
|
|
if (len1 > len2) return FALSE;
|
|
|
|
|
|
|
|
|
|
return (efHNLexOrder(hierName1, hierName2) > 0);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efHNLexOrder --
|
|
|
|
|
*
|
|
|
|
|
* Procedure to ensure that the canonical ordering used in determining
|
|
|
|
|
* preferred names is the same as would have been used if we were comparing
|
|
|
|
|
* the string version of two HierNames, instead of comparing them as pathnames
|
|
|
|
|
* with the last component first.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Same as strcmp(), i.e,
|
|
|
|
|
* -1 if hierName1 should precede hierName2 lexicographically,
|
|
|
|
|
* +1 if hierName1 should follow hierName2, and
|
|
|
|
|
* 0 is they are identical.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
efHNLexOrder(hierName1, hierName2)
|
|
|
|
|
HierName *hierName1, *hierName2;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (hierName1 == hierName2)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (hierName1->hn_parent)
|
|
|
|
|
if (i = efHNLexOrder(hierName1->hn_parent, hierName2->hn_parent))
|
|
|
|
|
return i;
|
|
|
|
|
|
|
|
|
|
return strcmp(hierName1->hn_name, hierName2->hn_name);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efHNFromUse --
|
|
|
|
|
*
|
|
|
|
|
* Construct a HierName for a cell use (for the array element identified
|
|
|
|
|
* by (hc_x, hc_y) if the use is an array). The parent pointer of this
|
|
|
|
|
* newly allocated HierName will be set to prefix.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to a newly allocated HierName.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
* Note: we use a hash table to ensure that we always return
|
|
|
|
|
* the SAME HierName whenever prefix, the (x, y) use
|
|
|
|
|
* coordinates, and the use id are the same.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
HierName *
|
|
|
|
|
efHNFromUse(hc, prefix)
|
|
|
|
|
HierContext *hc; /* Contains use and array information */
|
|
|
|
|
HierName *prefix; /* Root part of name */
|
|
|
|
|
{
|
|
|
|
|
char *srcp, *dstp;
|
|
|
|
|
char name[2048], *namePtr;
|
|
|
|
|
Use *u = hc->hc_use;
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
bool hasX, hasY;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
unsigned size;
|
|
|
|
|
|
|
|
|
|
hasX = u->use_xlo != u->use_xhi;
|
|
|
|
|
hasY = u->use_ylo != u->use_yhi;
|
|
|
|
|
namePtr = u->use_id;
|
|
|
|
|
if (hasX || hasY)
|
|
|
|
|
{
|
|
|
|
|
namePtr = name;
|
|
|
|
|
srcp = u->use_id;
|
|
|
|
|
dstp = name;
|
|
|
|
|
while (*dstp++ = *srcp++)
|
|
|
|
|
/* Nothing */;
|
|
|
|
|
|
|
|
|
|
/* Array subscript */
|
|
|
|
|
dstp[-1] = '[';
|
|
|
|
|
|
|
|
|
|
/* Y comes before X */
|
|
|
|
|
if (hasY)
|
|
|
|
|
{
|
|
|
|
|
(void) sprintf(dstp, "%d", hc->hc_y);
|
|
|
|
|
while (*dstp++)
|
|
|
|
|
/* Nothing */;
|
|
|
|
|
dstp--; /* Leave pointing to NULL byte */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasX)
|
|
|
|
|
{
|
|
|
|
|
if (hasY) *dstp++ = ',';
|
|
|
|
|
(void) sprintf(dstp, "%d", hc->hc_x);
|
|
|
|
|
while (*dstp++)
|
|
|
|
|
/* Nothing */;
|
|
|
|
|
dstp--; /* Leave pointing to NULL byte */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*dstp++ = ']';
|
|
|
|
|
*dstp = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size = HIERNAMESIZE(strlen(namePtr));
|
|
|
|
|
hierName = (HierName *) mallocMagic ((unsigned)(size));
|
|
|
|
|
if (efHNStats) efHNRecord(size, HN_FROMUSE);
|
|
|
|
|
efHNInit(hierName, namePtr, (char *) NULL);
|
|
|
|
|
hierName->hn_parent = prefix;
|
|
|
|
|
|
|
|
|
|
/* See if we already have an entry for this one */
|
|
|
|
|
he = HashFind(&efHNUseHashTable, (char *) hierName);
|
|
|
|
|
if (HashGetValue(he))
|
|
|
|
|
{
|
|
|
|
|
freeMagic((char *) hierName);
|
|
|
|
|
return (HierName *) HashGetValue(he);
|
|
|
|
|
}
|
|
|
|
|
HashSetValue(he, (ClientData) hierName);
|
|
|
|
|
|
|
|
|
|
(void) HashFind(&efFreeHashTable, (char *) hierName);
|
|
|
|
|
|
|
|
|
|
return hierName;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efHNUseCompare --
|
|
|
|
|
*
|
|
|
|
|
* Compare two HierNames for equality, but using a different sense
|
|
|
|
|
* of comparison than efHNCompare: two names are considered equal
|
|
|
|
|
* only if their hn_parent fields are equal and their hn_name strings
|
|
|
|
|
* are identical.
|
|
|
|
|
*
|
|
|
|
|
* Results: Returns 0 if they are equal, 1 if not.
|
|
|
|
|
*
|
|
|
|
|
* efHNUseHash --
|
|
|
|
|
*
|
|
|
|
|
* Convert a HierName to a single 32-bit value suitable for being
|
|
|
|
|
* turned into a hash bucket by the hash module. Hashes based on
|
|
|
|
|
* hierName->hn_hash and hierName->hn_parent, rather than summing
|
|
|
|
|
* the hn_hash values.
|
|
|
|
|
*
|
|
|
|
|
* Results: Returns the 32-bit hash value.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
efHNUseCompare(hierName1, hierName2)
|
|
|
|
|
HierName *hierName1, *hierName2;
|
|
|
|
|
{
|
|
|
|
|
return ((bool)(hierName1->hn_parent != hierName2->hn_parent
|
|
|
|
|
|| strcmp(hierName1->hn_name, hierName2->hn_name)
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
efHNUseHash(hierName)
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
{
|
|
|
|
|
return hierName->hn_hash + (spointertype) hierName->hn_parent;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efHNInit --
|
|
|
|
|
*
|
|
|
|
|
* Copy the string 'cp' into hierName->hn_name, also initializing
|
|
|
|
|
* the hn_hash fields of hierName. If 'endp' is NULL, copy all
|
|
|
|
|
* characters in 'cp' up to a trailing NULL byte; otherwise, copy
|
|
|
|
|
* up to 'endp'.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efHNInit(hierName, cp, endp)
|
|
|
|
|
HierName *hierName; /* Fill in fields of this HierName */
|
|
|
|
|
char *cp; /* Start of name to be stored in hn_name */
|
|
|
|
|
char *endp; /* End of name if non-NULL; else, see above */
|
|
|
|
|
{
|
|
|
|
|
unsigned hashsum;
|
|
|
|
|
char *dstp;
|
|
|
|
|
|
|
|
|
|
hashsum = 0;
|
|
|
|
|
dstp = hierName->hn_name;
|
|
|
|
|
if (endp)
|
|
|
|
|
{
|
|
|
|
|
while (cp < endp)
|
|
|
|
|
{
|
|
|
|
|
hashsum = HASHADDVAL(hashsum, *cp);
|
|
|
|
|
*dstp++ = *cp++;
|
|
|
|
|
}
|
|
|
|
|
*dstp = '\0';
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while (*dstp++ = *cp)
|
|
|
|
|
hashsum = HASHADDVAL(hashsum, *cp++);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hierName->hn_hash = hashsum;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efHNCompare --
|
|
|
|
|
*
|
|
|
|
|
* Compare two HierNames for equality. Passed as a client procedure
|
|
|
|
|
* to the hash module. The most likely place for a difference in the
|
|
|
|
|
* two names is in the lowest-level component, which fortunately is
|
|
|
|
|
* the first in a HierName list.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 0 if they are equal, 1 if not.
|
|
|
|
|
*
|
|
|
|
|
* efHNHash --
|
|
|
|
|
*
|
|
|
|
|
* Convert a HierName to a single 32-bit value suitable for being
|
|
|
|
|
* turned into a hash bucket by the hash module. Passed as a client
|
|
|
|
|
* procedure to the hash module.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns the 32-bit hash value.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
efHNCompare(hierName1, hierName2)
|
|
|
|
|
HierName *hierName1, *hierName2;
|
|
|
|
|
{
|
|
|
|
|
while (hierName1)
|
|
|
|
|
{
|
|
|
|
|
if (hierName1 == hierName2)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (hierName2 == NULL
|
|
|
|
|
|| hierName1->hn_hash != hierName2->hn_hash
|
|
|
|
|
|| strcmp(hierName1->hn_name, hierName2->hn_name) != 0)
|
|
|
|
|
return 1;
|
|
|
|
|
hierName1 = hierName1->hn_parent;
|
|
|
|
|
hierName2 = hierName2->hn_parent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (hierName2 ? 1 : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
efHNHash(hierName)
|
|
|
|
|
HierName *hierName;
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
for (n = 0; hierName; hierName = hierName->hn_parent)
|
|
|
|
|
n += hierName->hn_hash;
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efHNDistCompare --
|
|
|
|
|
* efHNDistCopy --
|
|
|
|
|
* efHNDistHash --
|
|
|
|
|
* efHNDistKill --
|
|
|
|
|
*
|
|
|
|
|
* Procedures for managing a HashTable whose keys are pointers
|
|
|
|
|
* to malloc'd Distance structures. Distances point to a pair of
|
|
|
|
|
* HierNames; the comparison and hashing functions rely directly
|
|
|
|
|
* on those for processing HierNames (efHNCompare() and efHNHash()).
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* efHNDistCompare returns 0 if the two keys are equal, 1 if not.
|
|
|
|
|
* efHNDistCopy returns a pointer to a malloc'd copy of its Distance
|
|
|
|
|
* argument.
|
|
|
|
|
* efHNDistHash returns a single 32-bit hash value based on a Distance's
|
|
|
|
|
* two HierNames.
|
|
|
|
|
* efHNDistKill has no return value.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* efHNDistKill frees its Distance argument, and adds the HierNames
|
|
|
|
|
* pointed to by it to the table of HierNames to free.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
efHNDistCompare(dist1, dist2)
|
|
|
|
|
Distance *dist1, *dist2;
|
|
|
|
|
{
|
|
|
|
|
return ((bool)(efHNCompare(dist1->dist_1, dist2->dist_1)
|
|
|
|
|
|| efHNCompare(dist1->dist_2, dist2->dist_2)
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
efHNDistCopy(dist)
|
|
|
|
|
Distance *dist;
|
|
|
|
|
{
|
|
|
|
|
Distance *distNew;
|
|
|
|
|
|
|
|
|
|
distNew = (Distance *) mallocMagic ((unsigned)(sizeof (Distance)));
|
|
|
|
|
*distNew = *dist;
|
|
|
|
|
return (char *) distNew;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
efHNDistHash(dist)
|
|
|
|
|
Distance *dist;
|
|
|
|
|
{
|
|
|
|
|
return efHNHash(dist->dist_1) + efHNHash(dist->dist_2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efHNDistKill(dist)
|
|
|
|
|
Distance *dist;
|
|
|
|
|
{
|
|
|
|
|
HierName *hn;
|
|
|
|
|
|
|
|
|
|
for (hn = dist->dist_1; hn; hn = hn->hn_parent)
|
|
|
|
|
(void) HashFind(&efFreeHashTable, (char *) hn);
|
|
|
|
|
for (hn = dist->dist_2; hn; hn = hn->hn_parent)
|
|
|
|
|
(void) HashFind(&efFreeHashTable, (char *) hn);
|
|
|
|
|
|
|
|
|
|
freeMagic((char *) dist);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efHNBuildDistKey --
|
|
|
|
|
*
|
|
|
|
|
* Build the key for looking in the Distance hash table for efFlatDists()
|
|
|
|
|
* above.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Sets up *distKey.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efHNBuildDistKey(prefix, dist, distKey)
|
|
|
|
|
HierName *prefix;
|
|
|
|
|
Distance *dist;
|
|
|
|
|
Distance *distKey;
|
|
|
|
|
{
|
|
|
|
|
HierName *hn1, *hn2;
|
|
|
|
|
|
|
|
|
|
hn1 = EFHNConcat(prefix, dist->dist_1);
|
|
|
|
|
hn2 = EFHNConcat(prefix, dist->dist_2);
|
|
|
|
|
if (EFHNBest(hn1, hn2))
|
|
|
|
|
{
|
|
|
|
|
distKey->dist_1 = hn1;
|
|
|
|
|
distKey->dist_2 = hn2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
distKey->dist_1 = hn2;
|
|
|
|
|
distKey->dist_2 = hn1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
distKey->dist_min = dist->dist_min;
|
|
|
|
|
distKey->dist_max = dist->dist_max;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efHNDump --
|
|
|
|
|
*
|
|
|
|
|
* Print all the names in the node hash table efNodeHashTable.
|
|
|
|
|
* Used for debugging.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Creates a file "hash.dump" and writes the node names to
|
|
|
|
|
* it, one per line.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efHNDump()
|
|
|
|
|
{
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
|
|
f = fopen("hash.dump", "w");
|
|
|
|
|
if (f == NULL)
|
|
|
|
|
{
|
|
|
|
|
perror("hash.dump");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while (he = HashNext(&efNodeHashTable, &hs))
|
|
|
|
|
fprintf(f, "%s\n", EFHNToStr((HierName *) he->h_key.h_ptr));
|
|
|
|
|
|
|
|
|
|
(void) fclose(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int efHNSizes[4];
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efHNRecord(size, type)
|
|
|
|
|
int size;
|
|
|
|
|
int type;
|
|
|
|
|
{
|
|
|
|
|
efHNSizes[type] += size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efHNPrintSizes(when)
|
|
|
|
|
char *when;
|
|
|
|
|
{
|
|
|
|
|
int total, i;
|
|
|
|
|
|
|
|
|
|
total = 0;
|
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
|
total += efHNSizes[i];
|
|
|
|
|
|
|
|
|
|
printf("Memory used in HierNames %s:\n", when ? when : "");
|
|
|
|
|
printf("%8d bytes for global names\n", efHNSizes[HN_GLOBAL]);
|
|
|
|
|
printf("%8d bytes for concatenated HierNames\n", efHNSizes[HN_CONCAT]);
|
|
|
|
|
printf("%8d bytes for names from cell uses\n", efHNSizes[HN_FROMUSE]);
|
|
|
|
|
printf("%8d bytes for names from strings\n", efHNSizes[HN_ALLOC]);
|
|
|
|
|
printf("--------\n");
|
|
|
|
|
printf("%8d bytes total\n", total);
|
|
|
|
|
}
|