2 /*--------------------------------------------------------------------*/
3 /*--- A program that merges multiple cachegrind output files. ---*/
5 /*--------------------------------------------------------------------*/
8 This file is part of Cachegrind, a Valgrind tool for cache
11 Copyright (C) 2002-2010 Nicholas Nethercote
14 AVL tree code derived from
15 ANSI C Library for maintainance of AVL Balanced Trees
16 (C) 2000 Daniel Nagy, Budapest University of Technology and Economics
17 Released under GNU General Public License (GPL) version 2
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation; either version 2 of the
22 License, or (at your option) any later version.
24 This program is distributed in the hope that it will be useful, but
25 WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to the Free Software
31 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
34 The GNU General Public License is contained in the file COPYING.
43 typedef signed long Word;
44 typedef unsigned long UWord;
45 typedef unsigned char Bool;
46 #define True ((Bool)1)
47 #define False ((Bool)0)
48 typedef signed int Int;
49 typedef unsigned int UInt;
50 typedef unsigned long long int ULong;
51 typedef signed char Char;
55 //------------------------------------------------------------------//
57 //--- Public interface ---//
58 //------------------------------------------------------------------//
60 typedef struct _WordFM WordFM; /* opaque */
62 /* Initialise a WordFM */
63 void initFM ( WordFM* t,
64 void* (*alloc_nofail)( SizeT ),
65 void (*dealloc)(void*),
66 Word (*kCmp)(Word,Word) );
68 /* Allocate and initialise a WordFM */
69 WordFM* newFM( void* (*alloc_nofail)( SizeT ),
70 void (*dealloc)(void*),
71 Word (*kCmp)(Word,Word) );
73 /* Free up the FM. If kFin is non-NULL, it is applied to keys
74 before the FM is deleted; ditto with vFin for vals. */
75 void deleteFM ( WordFM*, void(*kFin)(Word), void(*vFin)(Word) );
77 /* Add (k,v) to fm. If a binding for k already exists, it is updated
78 to map to this new v. In that case we should really return the
79 previous v so that caller can finalise it. Oh well. */
80 void addToFM ( WordFM* fm, Word k, Word v );
82 // Delete key from fm, returning associated val if found
83 Bool delFromFM ( WordFM* fm, /*OUT*/Word* oldV, Word key );
85 // Look up in fm, assigning found val at spec'd address
86 Bool lookupFM ( WordFM* fm, /*OUT*/Word* valP, Word key );
88 Word sizeFM ( WordFM* fm );
90 // set up FM for iteration
91 void initIterFM ( WordFM* fm );
93 // get next key/val pair. Will assert if fm has been modified
94 // or looked up in since initIterFM was called.
95 Bool nextIterFM ( WordFM* fm, /*OUT*/Word* pKey, /*OUT*/Word* pVal );
97 // clear the I'm iterating flag
98 void doneIterFM ( WordFM* fm );
100 // Deep copy a FM. If dopyK is NULL, keys are copied verbatim.
101 // If non-null, dopyK is applied to each key to generate the
102 // version in the new copy. In that case, if the argument to dopyK
103 // is non-NULL but the result is NULL, it is assumed that dopyK
104 // could not allocate memory, in which case the copy is abandoned
105 // and NULL is returned. Ditto with dopyV for values.
106 WordFM* dopyFM ( WordFM* fm, Word(*dopyK)(Word), Word(*dopyV)(Word) );
108 //------------------------------------------------------------------//
109 //--- end WordFM ---//
110 //--- Public interface ---//
111 //------------------------------------------------------------------//
114 static char* argv0 = "cg_merge";
116 /* Keep track of source filename/line no so as to be able to
117 print decent error messages. */
126 static void printSrcLoc ( SOURCE* s )
128 fprintf(stderr, "%s: near %s line %u\n", argv0, s->filename, s->lno-1);
131 __attribute__((noreturn))
132 static void mallocFail ( SOURCE* s, char* who )
134 fprintf(stderr, "%s: out of memory in %s\n", argv0, who );
139 __attribute__((noreturn))
140 static void parseError ( SOURCE* s, char* msg )
142 fprintf(stderr, "%s: parse error: %s\n", argv0, msg );
147 __attribute__((noreturn))
148 static void barf ( SOURCE* s, char* msg )
150 fprintf(stderr, "%s: %s\n", argv0, msg );
156 #define M_LINEBUF 40960
157 static char line[M_LINEBUF];
159 // True if anything read, False if at EOF
160 static Bool readline ( SOURCE* s )
165 if (i >= M_LINEBUF-10)
166 parseError(s, "Unexpected long line in input file");
179 barf(s, "I/O error while reading input file");
189 static Bool streqn ( char* s1, char* s2, size_t n )
191 return 0 == strncmp(s1, s2, n);
194 static Bool streq ( char* s1, char* s2 )
196 return 0 == strcmp(s1, s2 );
200 ////////////////////////////////////////////////////////////////
218 // null-terminated vector of desc_lines
228 // Summary line (copied from input)
232 WordFM FileFn* innerMap
233 where innerMap is WordFM line-number=UWord Counts */
236 // Summary counts (computed whilst parsing)
237 // should match .summary_line
242 static FileFn* new_FileFn ( char* file_name, char* fn_name )
244 FileFn* ffn = malloc(sizeof(FileFn));
247 ffn->fi_name = file_name;
248 ffn->fn_name = fn_name;
252 static void ddel_FileFn ( FileFn* ffn )
258 memset(ffn, 0, sizeof(FileFn));
262 static FileFn* dopy_FileFn ( FileFn* ff )
264 char* fi2 = strdup(ff->fi_name);
265 char* fn2 = strdup(ff->fn_name);
266 if ((!fi2) || (!fn2))
268 return new_FileFn( fi2, fn2 );
271 static Counts* new_Counts ( Int n_counts, /*COPIED*/ULong* counts )
274 Counts* cts = malloc(sizeof(Counts));
278 assert(n_counts >= 0);
279 cts->counts = malloc(n_counts * sizeof(ULong));
280 if (cts->counts == NULL)
283 cts->n_counts = n_counts;
284 for (i = 0; i < n_counts; i++)
285 cts->counts[i] = counts[i];
290 static Counts* new_Counts_Zeroed ( Int n_counts )
293 Counts* cts = malloc(sizeof(Counts));
297 assert(n_counts >= 0);
298 cts->counts = malloc(n_counts * sizeof(ULong));
299 if (cts->counts == NULL)
302 cts->n_counts = n_counts;
303 for (i = 0; i < n_counts; i++)
309 static void sdel_Counts ( Counts* cts )
311 memset(cts, 0, sizeof(Counts));
315 static void ddel_Counts ( Counts* cts )
319 memset(cts, 0, sizeof(Counts));
323 static Counts* dopy_Counts ( Counts* cts )
325 return new_Counts( cts->n_counts, cts->counts );
329 CacheProfFile* new_CacheProfFile ( char** desc_lines,
337 CacheProfFile* cpf = malloc(sizeof(CacheProfFile));
340 cpf->desc_lines = desc_lines;
341 cpf->cmd_line = cmd_line;
342 cpf->events_line = events_line;
343 cpf->n_events = n_events;
344 cpf->summary_line = summary_line;
345 cpf->outerMap = outerMap;
346 cpf->summary = summary;
350 static WordFM* dopy_InnerMap ( WordFM* innerMap )
352 return dopyFM ( innerMap, NULL,
353 (Word(*)(Word))dopy_Counts );
356 static void ddel_InnerMap ( WordFM* innerMap )
358 deleteFM( innerMap, NULL, (void(*)(Word))ddel_Counts );
361 static void ddel_CacheProfFile ( CacheProfFile* cpf )
364 if (cpf->desc_lines) {
365 for (p = cpf->desc_lines; *p; p++)
367 free(cpf->desc_lines);
371 if (cpf->events_line)
372 free(cpf->events_line);
373 if (cpf->summary_line)
374 free(cpf->summary_line);
376 deleteFM( cpf->outerMap, (void(*)(Word))ddel_FileFn,
377 (void(*)(Word))ddel_InnerMap );
379 ddel_Counts(cpf->summary);
381 memset(cpf, 0, sizeof(CacheProfFile));
385 static void showCounts ( FILE* f, Counts* c )
388 for (i = 0; i < c->n_counts; i++) {
389 fprintf(f, "%lld ", c->counts[i]);
393 static void show_CacheProfFile ( FILE* f, CacheProfFile* cpf )
402 for (d = cpf->desc_lines; *d; d++)
403 fprintf(f, "%s\n", *d);
404 fprintf(f, "%s\n", cpf->cmd_line);
405 fprintf(f, "%s\n", cpf->events_line);
407 initIterFM( cpf->outerMap );
408 while (nextIterFM( cpf->outerMap, (Word*)(&topKey), (Word*)(&topVal) )) {
409 fprintf(f, "fl=%s\nfn=%s\n",
410 topKey->fi_name, topKey->fn_name );
411 initIterFM( topVal );
412 while (nextIterFM( topVal, (Word*)(&subKey), (Word*)(&subVal) )) {
413 fprintf(f, "%ld ", subKey );
414 showCounts( f, subVal );
417 doneIterFM( topVal );
419 doneIterFM( cpf->outerMap );
421 //fprintf(f, "%s\n", cpf->summary_line);
422 fprintf(f, "summary:");
423 for (i = 0; i < cpf->summary->n_counts; i++)
424 fprintf(f, " %lld", cpf->summary->counts[i]);
428 ////////////////////////////////////////////////////////////////
430 static Word cmp_FileFn ( Word s1, Word s2 )
432 FileFn* ff1 = (FileFn*)s1;
433 FileFn* ff2 = (FileFn*)s2;
434 Word r = strcmp(ff1->fi_name, ff2->fi_name);
436 r = strcmp(ff1->fn_name, ff2->fn_name);
440 static Word cmp_unboxed_UWord ( Word s1, Word s2 )
442 UWord u1 = (UWord)s1;
443 UWord u2 = (UWord)s2;
444 if (u1 < u2) return -1;
445 if (u1 > u2) return 1;
449 ////////////////////////////////////////////////////////////////
451 static Bool parse_ULong ( /*OUT*/ULong* res, /*INOUT*/char** pptr)
455 while (isspace(*ptr)) ptr++;
456 if (!isdigit(*ptr)) {
457 return False; /* end of string, or junk */
461 while (isdigit(*ptr)) {
462 u64 = (u64 * 10) + (ULong)(*ptr - '0');
470 // str is a line of digits, starting with a line number. Parse it,
471 // returning the first number in *lnno and the rest in a newly
472 // allocated Counts struct. If lnno is non-NULL, treat the first
473 // number as a line number and assign it to *lnno instead of
474 // incorporating it in the counts array.
476 Counts* splitUpCountsLine ( SOURCE* s, /*OUT*/UWord* lnno, char* str )
484 ok = parse_ULong( &tmpC[n_tmpC], &str );
488 if (n_tmpC >= N_TMPC)
489 barf(s, "N_TMPC too low. Increase and recompile.");
492 parseError(s, "garbage in counts line");
493 if (lnno ? (n_tmpC < 2) : (n_tmpC < 1))
494 parseError(s, "too few counts in count line");
497 *lnno = (UWord)tmpC[0];
498 counts = new_Counts( n_tmpC-1, /*COPIED*/&tmpC[1] );
500 counts = new_Counts( n_tmpC, /*COPIED*/&tmpC[0] );
507 static void addCounts ( SOURCE* s, /*OUT*/Counts* counts1, Counts* counts2 )
510 if (counts1->n_counts != counts2->n_counts)
511 parseError(s, "addCounts: inconsistent number of counts");
512 for (i = 0; i < counts1->n_counts; i++)
513 counts1->counts[i] += counts2->counts[i];
516 static Bool addCountsToMap ( SOURCE* s,
518 UWord lnno, Counts* newCounts )
521 // look up lnno in the map. If none present, add a binding
522 // lnno->counts. If present, add counts to the existing entry.
523 if (lookupFM( counts_map, (Word*)(&oldCounts), (Word)lnno )) {
524 // merge with existing binding
525 addCounts( s, oldCounts, newCounts );
528 // create new binding
529 addToFM( counts_map, (Word)lnno, (Word)newCounts );
535 void handle_counts ( SOURCE* s,
537 char* fi, char* fn, char* newCountsStr )
545 if (0) printf("%s %s %s\n", fi, fn, newCountsStr );
548 newCounts = splitUpCountsLine( s, &lnno, newCountsStr );
550 // Did we get the right number?
551 if (newCounts->n_counts != cpf->n_events)
555 topKey = malloc(sizeof(FileFn));
557 topKey->fi_name = strdup(fi);
558 topKey->fn_name = strdup(fn);
560 if (! (topKey && topKey->fi_name && topKey->fn_name))
561 mallocFail(s, "handle_counts:");
564 if (lookupFM( cpf->outerMap, (Word*)(&countsMap), (Word)topKey )) {
565 // found it. Merge in new counts
566 freeNewCounts = addCountsToMap( s, countsMap, lnno, newCounts );
569 // not found in the top map. Create new entry
570 countsMap = newFM( malloc, free, cmp_unboxed_UWord );
573 addToFM( cpf->outerMap, (Word)topKey, (Word)countsMap );
574 freeNewCounts = addCountsToMap( s, countsMap, lnno, newCounts );
577 // also add to running summary total
578 addCounts( s, cpf->summary, newCounts );
580 // if safe to do so, free up the count vector
582 ddel_Counts(newCounts);
587 parseError(s, "# counts doesn't match # events");
591 /* Parse a complete file from the stream in 's'. If a parse error
592 happens, do not return; instead exit via parseError(). If an
593 out-of-memory condition happens, do not return; instead exit via
596 static CacheProfFile* parse_CacheProfFile ( SOURCE* s )
598 #define M_TMP_DESCLINES 10
602 char* tmp_desclines[M_TMP_DESCLINES];
604 int n_tmp_desclines = 0;
607 char* curr_fn_init = "???";
608 char* curr_fl_init = "???";
609 char* curr_fn = curr_fn_init;
610 char* curr_fl = curr_fl_init;
612 cpf = new_CacheProfFile( NULL, NULL, NULL, 0, NULL, NULL, NULL );
614 mallocFail(s, "parse_CacheProfFile(1)");
616 // Parse "desc:" lines
621 if (!streqn(line, "desc: ", 6))
623 if (n_tmp_desclines >= M_TMP_DESCLINES)
624 barf(s, "M_TMP_DESCLINES too low; increase and recompile");
625 tmp_desclines[n_tmp_desclines++] = strdup(line);
628 if (n_tmp_desclines == 0)
629 parseError(s, "parse_CacheProfFile: no DESC lines present");
631 cpf->desc_lines = malloc( (1+n_tmp_desclines) * sizeof(char*) );
632 if (cpf->desc_lines == NULL)
633 mallocFail(s, "parse_CacheProfFile(2)");
635 cpf->desc_lines[n_tmp_desclines] = NULL;
636 for (i = 0; i < n_tmp_desclines; i++)
637 cpf->desc_lines[i] = tmp_desclines[i];
640 if (!streqn(line, "cmd: ", 5))
641 parseError(s, "parse_CacheProfFile: no CMD line present");
643 cpf->cmd_line = strdup(line);
644 if (cpf->cmd_line == NULL)
645 mallocFail(s, "parse_CacheProfFile(3)");
647 // Parse "events:" line and figure out how many events there are
650 parseError(s, "parse_CacheProfFile: eof before EVENTS line");
651 if (!streqn(line, "events: ", 8))
652 parseError(s, "parse_CacheProfFile: no EVENTS line present");
654 // figure out how many events there are by counting the number
655 // of space-alphanum transitions in the events_line
656 cpf->events_line = strdup(line);
657 if (cpf->events_line == NULL)
658 mallocFail(s, "parse_CacheProfFile(3)");
661 assert(cpf->events_line[6] == ':');
662 for (p = &cpf->events_line[6]; *p; p++) {
663 if (p[0] == ' ' && isalpha(p[1]))
667 // create the running cross-check summary
668 cpf->summary = new_Counts_Zeroed( cpf->n_events );
669 if (cpf->summary == NULL)
670 mallocFail(s, "parse_CacheProfFile(4)");
672 // create the outer map (file+fn name --> inner map)
673 cpf->outerMap = newFM ( malloc, free, cmp_FileFn );
674 if (cpf->outerMap == NULL)
675 mallocFail(s, "parse_CacheProfFile(5)");
677 // process count lines
681 parseError(s, "parse_CacheProfFile: eof before SUMMARY line");
683 if (isdigit(line[0])) {
684 handle_counts(s, cpf, curr_fl, curr_fn, line);
688 if (streqn(line, "fn=", 3)) {
689 if (curr_fn != curr_fn_init)
691 curr_fn = strdup(line+3);
695 if (streqn(line, "fl=", 3)) {
696 if (curr_fl != curr_fl_init)
698 curr_fl = strdup(line+3);
702 if (streqn(line, "summary: ", 9)) {
706 parseError(s, "parse_CacheProfFile: unexpected line in main data");
709 // finally, the "summary:" line
710 if (!streqn(line, "summary: ", 9))
711 parseError(s, "parse_CacheProfFile: missing SUMMARY line");
713 cpf->summary_line = strdup(line);
714 if (cpf->summary_line == NULL)
715 mallocFail(s, "parse_CacheProfFile(6)");
717 // there should be nothing more
720 parseError(s, "parse_CacheProfFile: "
721 "extraneous content after SUMMARY line");
723 // check the summary counts are as expected
724 summaryRead = splitUpCountsLine( s, NULL, &cpf->summary_line[8] );
725 if (summaryRead == NULL)
726 mallocFail(s, "parse_CacheProfFile(7)");
727 if (summaryRead->n_counts != cpf->n_events)
728 parseError(s, "parse_CacheProfFile: wrong # counts in SUMMARY line");
729 for (i = 0; i < summaryRead->n_counts; i++) {
730 if (summaryRead->counts[i] != cpf->summary->counts[i]) {
731 parseError(s, "parse_CacheProfFile: "
732 "computed vs stated SUMMARY counts mismatch");
735 free(summaryRead->counts);
736 sdel_Counts(summaryRead);
738 // since the summary counts are OK, free up the summary_line text
739 // which contains the same info.
740 if (cpf->summary_line) {
741 free(cpf->summary_line);
742 cpf->summary_line = NULL;
745 if (curr_fn != curr_fn_init)
747 if (curr_fl != curr_fl_init)
753 #undef N_TMP_DESCLINES
757 static void merge_CacheProfInfo ( SOURCE* s,
758 /*MOD*/CacheProfFile* dst,
761 /* For each (filefn, innerMap) in src
763 add binding dopy(filefn)->dopy(innerMap) in src
765 // merge src->innerMap with dst->innerMap
766 for each (lineno, counts) in src->innerMap
767 if lineno not in dst->innerMap
768 add binding lineno->dopy(counts) to dst->innerMap
770 add counts into dst->innerMap[lineno]
772 /* Outer iterator: FileFn* -> WordFM* (inner iterator)
773 Inner iterator: UWord -> Counts*
782 /* First check mundane things: that the events: lines are
784 if (!streq( dst->events_line, src->events_line ))
785 barf(s, "\"events:\" line of most recent file does "
786 "not match those previously processed");
788 initIterFM( src->outerMap );
790 // for (filefn, innerMap) in src
791 while (nextIterFM( src->outerMap, (Word*)&soKey, (Word*)&soVal )) {
794 if (! lookupFM( dst->outerMap, (Word*)&doVal, (Word)soKey )) {
796 // no .. add dopy(filefn) -> dopy(innerMap) to src
797 FileFn* c_soKey = dopy_FileFn(soKey);
798 WordFM* c_soVal = dopy_InnerMap(soVal);
799 if ((!c_soKey) || (!c_soVal)) goto oom;
800 addToFM( dst->outerMap, (Word)c_soKey, (Word)c_soVal );
804 // yes .. merge the two innermaps
807 // for (lno, counts) in soVal (source inner map)
808 while (nextIterFM( soVal, (Word*)&siKey, (Word*)&siVal )) {
810 // is lno in the corresponding dst inner map?
811 if (! lookupFM( doVal, (Word*)&diVal, siKey )) {
813 // no .. add lineno->dopy(counts) to dst inner map
814 Counts* c_siVal = dopy_Counts( siVal );
815 if (!c_siVal) goto oom;
816 addToFM( doVal, siKey, (Word)c_siVal );
820 // yes .. merge counts into dst inner map val
821 addCounts( s, diVal, siVal );
830 // add the summaries too
831 addCounts(s, dst->summary, src->summary );
836 mallocFail(s, "merge_CacheProfInfo");
839 static void usage ( void )
841 fprintf(stderr, "%s: Merges multiple cachegrind output files into one\n",
843 fprintf(stderr, "%s: usage: %s [-o outfile] [files-to-merge]\n",
848 int main ( int argc, char** argv )
852 CacheProfFile *cpf, *cpfTmp;
854 FILE* outfile = NULL;
855 char* outfilename = NULL;
864 for (i = 1; i < argc; i++) {
865 if (streq(argv[i], "-h") || streq(argv[i], "--help"))
869 /* Scan args, looking for '-o outfilename'. */
870 for (i = 1; i < argc; i++) {
871 if (streq(argv[i], "-o")) {
873 outfilename = argv[i+1];
884 for (i = 1; i < argc; i++) {
886 if (i == outfileix) {
887 /* Skip '-o' and whatever follows it */
892 fprintf(stderr, "%s: parsing %s\n", argv0, argv[i]);
894 src.filename = argv[i];
895 src.fp = fopen(src.filename, "r");
898 barf(&src, "Cannot open input file");
901 cpfTmp = parse_CacheProfFile( &src );
904 /* If this isn't the first file, merge */
906 /* this is the first file */
909 /* not the first file; merge */
910 fprintf(stderr, "%s: merging %s\n", argv0, argv[i]);
911 merge_CacheProfInfo( &src, cpf, cpfTmp );
912 ddel_CacheProfFile( cpfTmp );
917 /* Now create the output file. */
921 fprintf(stderr, "%s: writing %s\n",
922 argv0, outfilename ? outfilename : "(stdout)" );
924 /* Write the output. */
926 outfile = fopen(outfilename, "w");
928 fprintf(stderr, "%s: can't create output file %s\n",
937 show_CacheProfFile( outfile, cpf );
938 if (ferror(outfile)) {
939 fprintf(stderr, "%s: error writing output file %s\n",
942 if (outfile != stdout)
948 if (outfile != stdout)
951 ddel_CacheProfFile( cpf );
958 //------------------------------------------------------------------//
960 //--- Implementation ---//
961 //------------------------------------------------------------------//
963 /* ------------ Implementation ------------ */
965 /* One element of the AVL tree */
970 struct _AvlNode* left;
971 struct _AvlNode* right;
983 #define WFM_STKMAX 32 // At most 2**32 entries can be iterated over
987 void* (*alloc_nofail)( SizeT );
988 void (*dealloc)(void*);
989 Word (*kCmp)(Word,Word);
990 AvlNode* nodeStack[WFM_STKMAX]; // Iterator node stack
991 Int numStack[WFM_STKMAX]; // Iterator num stack
992 Int stackTop; // Iterator stack pointer, one past end
996 static Bool avl_removeroot_wrk(AvlNode** t, Word(*kCmp)(Word,Word));
998 /* Swing to the left. Warning: no balance maintainance. */
999 static void avl_swl ( AvlNode** root )
1002 AvlNode* b = a->right;
1008 /* Swing to the right. Warning: no balance maintainance. */
1009 static void avl_swr ( AvlNode** root )
1012 AvlNode* b = a->left;
1018 /* Balance maintainance after especially nasty swings. */
1019 static void avl_nasty ( AvlNode* root )
1021 switch (root->balance) {
1023 root->left->balance = 0;
1024 root->right->balance = 1;
1027 root->left->balance = -1;
1028 root->right->balance = 0;
1031 root->left->balance = 0;
1032 root->right->balance = 0;
1040 /* Find size of a non-NULL tree. */
1041 static Word size_avl_nonNull ( AvlNode* nd )
1043 return 1 + (nd->left ? size_avl_nonNull(nd->left) : 0)
1044 + (nd->right ? size_avl_nonNull(nd->right) : 0);
1047 /* Insert element a into the AVL tree t. Returns True if the depth of
1048 the tree has grown. If element with that key is already present,
1049 just copy a->val to existing node, first returning old ->val field
1050 of existing node in *oldV, so that the caller can finalize it
1054 Bool avl_insert_wrk ( AvlNode** rootp,
1055 /*OUT*/MaybeWord* oldV,
1057 Word (*kCmp)(Word,Word) )
1067 /* insert into an empty tree? */
1073 cmpres = kCmp( (*rootp)->key, a->key );
1076 /* insert into the left subtree */
1077 if ((*rootp)->left) {
1078 AvlNode* left_subtree = (*rootp)->left;
1079 if (avl_insert_wrk(&left_subtree, oldV, a, kCmp)) {
1080 switch ((*rootp)->balance--) {
1081 case 1: return False;
1082 case 0: return True;
1086 if ((*rootp)->left->balance < 0) {
1088 (*rootp)->balance = 0;
1089 (*rootp)->right->balance = 0;
1091 avl_swl( &((*rootp)->left) );
1093 avl_nasty( *rootp );
1096 (*rootp)->left = left_subtree;
1101 if ((*rootp)->balance--)
1105 assert(0);/*NOTREACHED*/
1109 /* insert into the right subtree */
1110 if ((*rootp)->right) {
1111 AvlNode* right_subtree = (*rootp)->right;
1112 if (avl_insert_wrk(&right_subtree, oldV, a, kCmp)) {
1113 switch((*rootp)->balance++){
1114 case -1: return False;
1115 case 0: return True;
1119 if ((*rootp)->right->balance > 0) {
1121 (*rootp)->balance = 0;
1122 (*rootp)->left->balance = 0;
1124 avl_swr( &((*rootp)->right) );
1126 avl_nasty( *rootp );
1129 (*rootp)->right = right_subtree;
1133 (*rootp)->right = a;
1134 if ((*rootp)->balance++)
1138 assert(0);/*NOTREACHED*/
1141 /* cmpres == 0, a duplicate - replace the val, but don't
1142 incorporate the node in the tree */
1144 oldV->w = (*rootp)->val;
1145 (*rootp)->val = a->val;
1150 /* Remove an element a from the AVL tree t. a must be part of
1151 the tree. Returns True if the depth of the tree has shrunk.
1154 Bool avl_remove_wrk ( AvlNode** rootp,
1156 Word(*kCmp)(Word,Word) )
1159 Word cmpres = kCmp( (*rootp)->key, a->key );
1162 /* remove from the left subtree */
1163 AvlNode* left_subtree = (*rootp)->left;
1164 assert(left_subtree);
1165 ch = avl_remove_wrk(&left_subtree, a, kCmp);
1166 (*rootp)->left=left_subtree;
1168 switch ((*rootp)->balance++) {
1169 case -1: return True;
1170 case 0: return False;
1174 switch ((*rootp)->right->balance) {
1177 (*rootp)->balance = -1;
1178 (*rootp)->left->balance = 1;
1182 (*rootp)->balance = 0;
1183 (*rootp)->left->balance = 0;
1190 avl_swr( &((*rootp)->right) );
1192 avl_nasty( *rootp );
1198 /* remove from the right subtree */
1199 AvlNode* right_subtree = (*rootp)->right;
1200 assert(right_subtree);
1201 ch = avl_remove_wrk(&right_subtree, a, kCmp);
1202 (*rootp)->right = right_subtree;
1204 switch ((*rootp)->balance--) {
1205 case 1: return True;
1206 case 0: return False;
1210 switch ((*rootp)->left->balance) {
1213 (*rootp)->balance = 1;
1214 (*rootp)->right->balance = -1;
1218 (*rootp)->balance = 0;
1219 (*rootp)->right->balance = 0;
1226 avl_swl( &((*rootp)->left) );
1228 avl_nasty( *rootp );
1233 assert(cmpres == 0);
1234 assert((*rootp)==a);
1235 return avl_removeroot_wrk(rootp, kCmp);
1240 /* Remove the root of the AVL tree *rootp.
1241 * Warning: dumps core if *rootp is empty
1244 Bool avl_removeroot_wrk ( AvlNode** rootp,
1245 Word(*kCmp)(Word,Word) )
1249 if (!(*rootp)->left) {
1250 if (!(*rootp)->right) {
1254 (*rootp) = (*rootp)->right;
1257 if (!(*rootp)->right) {
1258 (*rootp) = (*rootp)->left;
1261 if ((*rootp)->balance < 0) {
1262 /* remove from the left subtree */
1264 while (a->right) a = a->right;
1266 /* remove from the right subtree */
1267 a = (*rootp)->right;
1268 while (a->left) a = a->left;
1270 ch = avl_remove_wrk(rootp, a, kCmp);
1271 a->left = (*rootp)->left;
1272 a->right = (*rootp)->right;
1273 a->balance = (*rootp)->balance;
1275 if(a->balance == 0) return ch;
1280 AvlNode* avl_find_node ( AvlNode* t, Word k, Word(*kCmp)(Word,Word) )
1284 if (t == NULL) return NULL;
1285 cmpres = kCmp(t->key, k);
1286 if (cmpres > 0) t = t->left; else
1287 if (cmpres < 0) t = t->right; else
1292 // Clear the iterator stack.
1293 static void stackClear(WordFM* fm)
1297 for (i = 0; i < WFM_STKMAX; i++) {
1298 fm->nodeStack[i] = NULL;
1299 fm->numStack[i] = 0;
1304 // Push onto the iterator stack.
1305 static inline void stackPush(WordFM* fm, AvlNode* n, Int i)
1307 assert(fm->stackTop < WFM_STKMAX);
1308 assert(1 <= i && i <= 3);
1309 fm->nodeStack[fm->stackTop] = n;
1310 fm-> numStack[fm->stackTop] = i;
1314 // Pop from the iterator stack.
1315 static inline Bool stackPop(WordFM* fm, AvlNode** n, Int* i)
1317 assert(fm->stackTop <= WFM_STKMAX);
1319 if (fm->stackTop > 0) {
1321 *n = fm->nodeStack[fm->stackTop];
1322 *i = fm-> numStack[fm->stackTop];
1323 assert(1 <= *i && *i <= 3);
1324 fm->nodeStack[fm->stackTop] = NULL;
1325 fm-> numStack[fm->stackTop] = 0;
1333 AvlNode* avl_dopy ( AvlNode* nd,
1336 void*(alloc_nofail)(SizeT) )
1341 nyu = alloc_nofail(sizeof(AvlNode));
1344 nyu->left = nd->left;
1345 nyu->right = nd->right;
1346 nyu->balance = nd->balance;
1350 nyu->key = dopyK( nd->key );
1351 if (nd->key != 0 && nyu->key == 0)
1352 return NULL; /* oom in key dcopy */
1354 /* copying assumedly unboxed keys */
1360 nyu->val = dopyV( nd->val );
1361 if (nd->val != 0 && nyu->val == 0)
1362 return NULL; /* oom in val dcopy */
1364 /* copying assumedly unboxed vals */
1370 nyu->left = avl_dopy( nyu->left, dopyK, dopyV, alloc_nofail );
1375 nyu->right = avl_dopy( nyu->right, dopyK, dopyV, alloc_nofail );
1383 /* --- Public interface functions --- */
1385 /* Initialise a WordFM. */
1386 void initFM ( WordFM* fm,
1387 void* (*alloc_nofail)( SizeT ),
1388 void (*dealloc)(void*),
1389 Word (*kCmp)(Word,Word) )
1393 fm->alloc_nofail = alloc_nofail;
1394 fm->dealloc = dealloc;
1398 /* Allocate and Initialise a WordFM. */
1399 WordFM* newFM( void* (*alloc_nofail)( SizeT ),
1400 void (*dealloc)(void*),
1401 Word (*kCmp)(Word,Word) )
1403 WordFM* fm = alloc_nofail(sizeof(WordFM));
1405 initFM(fm, alloc_nofail, dealloc, kCmp);
1409 static void avl_free ( AvlNode* nd,
1412 void(*dealloc)(void*) )
1417 avl_free(nd->left, kFin, vFin, dealloc);
1419 avl_free(nd->right, kFin, vFin, dealloc);
1424 memset(nd, 0, sizeof(AvlNode));
1428 /* Free up the FM. If kFin is non-NULL, it is applied to keys
1429 before the FM is deleted; ditto with vFin for vals. */
1430 void deleteFM ( WordFM* fm, void(*kFin)(Word), void(*vFin)(Word) )
1432 void(*dealloc)(void*) = fm->dealloc;
1433 avl_free( fm->root, kFin, vFin, dealloc );
1434 memset(fm, 0, sizeof(WordFM) );
1438 /* Add (k,v) to fm. */
1439 void addToFM ( WordFM* fm, Word k, Word v )
1443 node = fm->alloc_nofail( sizeof(struct _AvlNode) );
1448 avl_insert_wrk( &fm->root, &oldV, node, fm->kCmp );
1449 //if (oldV.b && fm->vFin)
1450 // fm->vFin( oldV.w );
1455 // Delete key from fm, returning associated val if found
1456 Bool delFromFM ( WordFM* fm, /*OUT*/Word* oldV, Word key )
1458 AvlNode* node = avl_find_node( fm->root, key, fm->kCmp );
1460 avl_remove_wrk( &fm->root, node, fm->kCmp );
1470 // Look up in fm, assigning found val at spec'd address
1471 Bool lookupFM ( WordFM* fm, /*OUT*/Word* valP, Word key )
1473 AvlNode* node = avl_find_node( fm->root, key, fm->kCmp );
1483 Word sizeFM ( WordFM* fm )
1485 // Hmm, this is a bad way to do this
1486 return fm->root ? size_avl_nonNull( fm->root ) : 0;
1489 // set up FM for iteration
1490 void initIterFM ( WordFM* fm )
1495 stackPush(fm, fm->root, 1);
1498 // get next key/val pair. Will assert if fm has been modified
1499 // or looked up in since initIterFM was called.
1500 Bool nextIterFM ( WordFM* fm, /*OUT*/Word* pKey, /*OUT*/Word* pVal )
1507 // This in-order traversal requires each node to be pushed and popped
1508 // three times. These could be avoided by updating nodes in-situ on the
1509 // top of the stack, but the push/pop cost is so small that it's worth
1510 // keeping this loop in this simpler form.
1511 while (stackPop(fm, &n, &i)) {
1514 stackPush(fm, n, 2);
1515 if (n->left) stackPush(fm, n->left, 1);
1518 stackPush(fm, n, 3);
1519 if (pKey) *pKey = n->key;
1520 if (pVal) *pVal = n->val;
1523 if (n->right) stackPush(fm, n->right, 1);
1530 // Stack empty, iterator is exhausted, return NULL
1534 // clear the I'm iterating flag
1535 void doneIterFM ( WordFM* fm )
1539 WordFM* dopyFM ( WordFM* fm, Word(*dopyK)(Word), Word(*dopyV)(Word) )
1543 /* can't clone the fm whilst iterating on it */
1544 assert(fm->stackTop == 0);
1546 nyu = fm->alloc_nofail( sizeof(WordFM) );
1552 memset(fm->nodeStack, 0, sizeof(fm->nodeStack));
1553 memset(fm->numStack, 0, sizeof(fm->numStack));
1556 nyu->root = avl_dopy( nyu->root, dopyK, dopyV, fm->alloc_nofail );
1564 //------------------------------------------------------------------//
1565 //--- end WordFM ---//
1566 //--- Implementation ---//
1567 //------------------------------------------------------------------//
1569 /*--------------------------------------------------------------------*/
1570 /*--- end cg_merge.c ---*/
1571 /*--------------------------------------------------------------------*/