/******************************************************************************* * Copyright 1991-1996 by ORCA Software, Inc. * * * * All rights reserved. May not be reproduced or distributed, in printed or * * electronic form, without permission of ORCA Software, Inc. May not be * * distributed as object code, separately or linked with other object modules, * * without permission. * *******************************************************************************/ /* ERROR CODES 20201-20201 */ #define XI_INTERNAL #include "xi.h" #include "xiheap.h" #include "xiutils.h" #define PADCHAR ((char)0x8e) #define MAGIC 0xe8 /* constants to define for more debugging PAD - pad allocations with N bytes of magic and check magic number when xi_tree_check_sanity is called FENCE - call xi_tree_check_sanity on every Nth xi_tree_* call PREPAD - put an extra N bytes between the node struct and the data. This padding is never checked. sample usage: #define PAD 20 #define FENCE 10 #define PREPAD 10 */ /* #define PAD 100 #define FENCE 1 */ typedef struct _tree_node { struct _tree_node *parent; struct _tree_node *sibling; struct _tree_node *child; #ifdef TREEDEBUG int line; char *file; int magic; /* for sanity checks */ int size; #ifdef PREPAD char prepad[PREPAD]; #endif #endif } TREE_NODE; /* This code automatically determines the proper size for TREE_NODE so that the data following it is always correctly aligned. */ static struct { TREE_NODE node; double data; } dummy_header; #define SIZEOF_TREE_NODE ((int)( (char*)&dummy_header.data - (char*)&dummy_header.node )) #ifdef TREEDEBUG #undef xi_tree_malloc #undef xi_tree_realloc #define xi_tree_malloc_body xi_tree_malloc_d #define xi_tree_realloc_body xi_tree_realloc_d #define xi_tree_malloc_stub xi_tree_malloc #define xi_tree_realloc_stub xi_tree_realloc #else #define xi_tree_malloc_body xi_tree_malloc #define xi_tree_realloc_body xi_tree_realloc #define xi_tree_malloc_stub xi_tree_malloc_d #define xi_tree_realloc_stub xi_tree_realloc_d #endif #define VOIDPTR_TO_TREEPTR(p) ((TREE_NODE *)((long)(p) - SIZEOF_TREE_NODE)) #define TREEPTR_TO_VOIDPTR(t) ((void *)((long)(t) + SIZEOF_TREE_NODE)) #define DBGTAB 3 static TREE_NODE topnode = { NULL, &topnode, NULL #ifdef TREEDEBUG ,0, "TOPNODE", MAGIC, 0 #endif }; static TREE_NODE *top = &topnode; #ifdef TREEDEBUG #ifdef FENCE static int fence_count; #endif static int node_count = 1; /* start with just top node */ #endif static void dflt_error_fcn( void ) { NOREF( dummy_header.data ); XinError( 20201, XinSeverityFatal, 0L ); } static void ( *error_fcn ) ( void ) = dflt_error_fcn; #ifdef TREEDEBUG void xi_tree_check_fence( void ); void xi_tree_check_fence( void ) { #ifdef FENCE static int fence_count; fence_count++; if ( fence_count % FENCE == 0 ) xi_tree_check_sanity( "FENCE CHECK" ); #endif } static void adjust_size( size_t * size ) { NOREF( size ); #ifdef PAD *size += PAD; #endif } static void validate_node( void *p ) { if ( p != NULL && VOIDPTR_TO_TREEPTR( p )->magic != MAGIC ) XinDialogFatal( "Bad tree node detected: file %s, line %d", VOIDPTR_TO_TREEPTR( p )->file, VOIDPTR_TO_TREEPTR( p )->line ); } #endif static void remove_from_siblings_and_parent( TREE_NODE * remove_t ) { TREE_NODE *tp; /* remove from sibling list */ tp = remove_t; while ( tp->sibling != remove_t ) tp = tp->sibling; tp->sibling = remove_t->sibling; /* adjust parent pointers */ if ( remove_t->parent->child == remove_t ) { remove_t->parent->child = ( ( remove_t->sibling == remove_t ) ? NULL : remove_t->sibling ); } } static void xi_tree_free_internal( TREE_NODE * remove_t, BOOLEAN toplevel ) { TREE_NODE *tp; TREE_NODE *nexttp; if ( toplevel ) { remove_from_siblings_and_parent( remove_t ); remove_t->sibling = NULL; } /* free all child nodes */ tp = remove_t->child; if ( tp != NULL ) do { /* store next pointer before nuking node */ nexttp = tp->sibling; xi_tree_free_internal( tp, FALSE ); tp = nexttp; } while ( tp != remove_t->child ); /* free underlying heap manager memory */ #ifdef TREEDEBUG heap_free( remove_t->file ); node_count--; #endif heap_free( remove_t ); } void xi_tree_reparent( void *p, void *parent ) { TREE_NODE *tn; #ifdef TREEDEBUG xi_tree_check_fence( ); validate_node( p ); #endif remove_from_siblings_and_parent( VOIDPTR_TO_TREEPTR( p ) ); tn = VOIDPTR_TO_TREEPTR( p ); if ( parent == NULL ) parent = TREEPTR_TO_VOIDPTR( top ); tn->parent = VOIDPTR_TO_TREEPTR( parent ); if ( tn->parent->child == NULL ) { tn->parent->child = tn; tn->sibling = tn; } else { /* insert tn in the sibling list */ tn->sibling = tn->parent->child->sibling; tn->parent->child->sibling = tn; } } void xi_tree_free( void *p ) { #ifdef TREEDEBUG xi_tree_check_fence( ); validate_node( p ); #endif xi_tree_free_internal( VOIDPTR_TO_TREEPTR( p ), TRUE ); } void * xi_tree_get_parent( void *p ) { TREE_NODE *parent; #ifdef TREEDEBUG validate_node( p ); #endif parent = VOIDPTR_TO_TREEPTR( p )->parent; return ( ( parent == top ) ? NULL : TREEPTR_TO_VOIDPTR( parent ) ); } void * xi_tree_malloc_body( size_t size, void *parent #ifdef TREEDEBUG ,int line, char *file #endif ) { TREE_NODE *tn; #ifdef TREEDEBUG size_t orig_size = size; xi_tree_check_fence( ); validate_node( parent ); adjust_size( &size ); #endif tn = ( TREE_NODE * ) heap_malloc( size + SIZEOF_TREE_NODE ); if ( !tn ) { ( *error_fcn ) ( ); return NULL; } #ifdef TREEDEBUG memset( ( char * ) tn + SIZEOF_TREE_NODE + orig_size, PADCHAR, ( long ) ( size - orig_size ) ); tn->file = ( char * ) heap_malloc( strlen( file ) + 1 ); if ( !tn->file ) { ( *error_fcn ) ( ); return NULL; } strcpy( tn->file, file ); tn->line = line; tn->magic = MAGIC; tn->size = size; node_count++; #endif tn->child = NULL; tn->sibling = tn; if ( parent == NULL ) parent = TREEPTR_TO_VOIDPTR( top ); tn->parent = VOIDPTR_TO_TREEPTR( parent ); if ( tn->parent->child == NULL ) tn->parent->child = tn; else { /* insert tn in the sibling list */ tn->sibling = tn->parent->child->sibling; tn->parent->child->sibling = tn; } return ( TREEPTR_TO_VOIDPTR( tn ) ); } void * xi_tree_realloc_body( void *p, size_t size #ifdef TREEDEBUG ,int line, char *file #endif ) { TREE_NODE *old_t; TREE_NODE *new_t; TREE_NODE *tp; #ifdef TREEDEBUG size_t orig_size = size; xi_tree_check_fence( ); adjust_size( &size ); #endif old_t = VOIDPTR_TO_TREEPTR( p ); #ifdef TREEDEBUG validate_node( p ); #endif new_t = ( TREE_NODE * ) heap_realloc( old_t, ( size + SIZEOF_TREE_NODE ) ); #ifdef TREEDEBUG if ( !new_t ) { ( *error_fcn ) ( ); return NULL; } memset( ( char * ) new_t + SIZEOF_TREE_NODE + orig_size, PADCHAR, ( long ) ( size - orig_size ) ); new_t->line = line; new_t->size = size; heap_free( new_t->file ); new_t->file = ( char * ) heap_malloc( strlen( file ) + 1 ); if ( !new_t->file ) { ( *error_fcn ) ( ); return NULL; } strcpy( new_t->file, file ); #endif if ( new_t != old_t ) { /* change parent pointer */ if ( new_t->parent->child == old_t ) new_t->parent->child = new_t; /* change sibling pointers */ for ( tp = new_t; tp->sibling != old_t; tp = tp->sibling ) ; tp->sibling = new_t; /* change children pointers */ tp = new_t->child; if ( tp != NULL ) do { tp->parent = new_t; tp = tp->sibling; } while ( tp != new_t->child ); } return ( TREEPTR_TO_VOIDPTR( new_t ) ) ; } static void xi_tree_dbg_internal( TREE_NODE * tn, int level ) { char buf[150]; char *s; TREE_NODE *tn2; int i, l; if ( tn == NULL ) return; /* print tab indent indicating level */ s = buf; for ( l = level; l; l-- ) { for ( i = 0; i < DBGTAB; i++ ) *s++ = ' '; } #ifdef TREEDEBUG sprintf( s, "node %08lx: par=%08lx, sib=%08lx, ch=%08lx, file=%s, line=%d", ( long ) ( tn ), ( long ) ( tn->parent ), ( long ) ( tn->sibling ), ( long ) ( tn->child ), tn->file, tn->line ); #else sprintf( s, "node %08lx: par=%08lx, sib=%08lx, ch=%08lx", ( long ) tn, ( long ) ( tn->parent ), ( long ) ( tn->sibling ), ( long ) ( tn->child ) ); #endif xi_dbg( buf ); tn2 = tn->child; if ( tn2 != NULL ) do { xi_tree_dbg_internal( tn2, level + 1 ); tn2 = tn2->sibling; } while ( tn2 != tn->child ); } void xi_tree_dbg( char *title ) { char buf[100]; sprintf( buf, "TREE MEMORY DEBUG TRACE (%s)", title ); xi_dbg( buf ); xi_dbg( "=======================" ); xi_tree_dbg_internal( top, 0 ); heap_dbg( title ); xi_tree_check_sanity( title ); } static void xi_tree_check_sanity_internal( TREE_NODE * tn, char *title, int *count ) { TREE_NODE *tn2; #ifdef TREEDEBUG #ifdef PAD int i; /* check pad bytes for node */ if ( tn != &topnode ) for ( i = tn->size - PAD; i < tn->size; i++ ) if ( *( ( char * ) tn + SIZEOF_TREE_NODE + i ) != PADCHAR ) XinDialogFatal( "xi_tree_node padding corrupted: node=%08lx, file=%s, line=%d", ( long ) ( tn ), tn->file, tn->line ); #endif #endif /* check that all children point to this node */ tn2 = tn->child; ( *count )++; if ( tn2 != NULL ) do { if ( tn2->parent != tn ) XinDialogFatal( "memory check %s: tree node %08lx has bad parent", title, ( long ) ( tn2 ) ); xi_tree_check_sanity_internal( tn2, title, count ); tn2 = tn2->sibling; } while ( tn2 != tn->child ); } void xi_tree_check_sanity( char *title ) { int count = 0; xi_tree_check_sanity_internal( top, title, &count ); #ifdef TREEDEBUG if ( count != node_count ) XinDialogFatal( "tree sanity check failed: tree count=%d, allocation count=%d", count, node_count ); #endif } #ifdef TREEDEBUG void *xi_tree_malloc_stub( size_t size, void *parent ); void * xi_tree_malloc_stub( size_t size, void *parent ) { return ( xi_tree_malloc_body( size, parent, 0, "(unknown)" ) ); } void *xi_tree_realloc_stub( void *p, size_t size ); void * xi_tree_realloc_stub( void *p, size_t size ) { return ( xi_tree_realloc_body( p, size, 0, "(unknown)" ) ); } #else void * xi_tree_malloc_stub( size_t size, void *parent, int line, char *file ); void * xi_tree_malloc_stub( size_t size, void *parent, int line, char *file ) { NOREF( line ); NOREF( file ); return ( xi_tree_malloc_body( size, parent ) ); } void * xi_tree_realloc_stub( void *p, size_t size, int line, char *file ); void * xi_tree_realloc_stub( void *p, size_t size, int line, char *file ) { NOREF( line ); NOREF( file ); return ( xi_tree_realloc_body( p, size ) ); } #endif void xi_tree_reg_error_fcn( void ( *fcn ) ( void ) ) { error_fcn = fcn; }