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-2013 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 const 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
, const char* who
)
134 fprintf(stderr
, "%s: out of memory in %s\n", argv0
, who
);
139 __attribute__((noreturn
))
140 static void parseError ( SOURCE
* s
, const char* msg
)
142 fprintf(stderr
, "%s: parse error: %s\n", argv0
, msg
);
147 __attribute__((noreturn
))
148 static void barf ( SOURCE
* s
, const 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 ( const char* s1
, const char* s2
, size_t n
)
191 return 0 == strncmp(s1
, s2
, n
);
194 static Bool
streq ( const char* s1
, const 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
) {
285 cts
->n_counts
= n_counts
;
286 for (i
= 0; i
< n_counts
; i
++)
287 cts
->counts
[i
] = counts
[i
];
292 static Counts
* new_Counts_Zeroed ( Int n_counts
)
295 Counts
* cts
= malloc(sizeof(Counts
));
299 assert(n_counts
>= 0);
300 cts
->counts
= malloc(n_counts
* sizeof(ULong
));
301 if (cts
->counts
== NULL
) {
306 cts
->n_counts
= n_counts
;
307 for (i
= 0; i
< n_counts
; i
++)
313 static void sdel_Counts ( Counts
* cts
)
315 memset(cts
, 0, sizeof(Counts
));
319 static void ddel_Counts ( Counts
* cts
)
323 memset(cts
, 0, sizeof(Counts
));
327 static Counts
* dopy_Counts ( Counts
* cts
)
329 return new_Counts( cts
->n_counts
, cts
->counts
);
333 CacheProfFile
* new_CacheProfFile ( char** desc_lines
,
341 CacheProfFile
* cpf
= malloc(sizeof(CacheProfFile
));
344 cpf
->desc_lines
= desc_lines
;
345 cpf
->cmd_line
= cmd_line
;
346 cpf
->events_line
= events_line
;
347 cpf
->n_events
= n_events
;
348 cpf
->summary_line
= summary_line
;
349 cpf
->outerMap
= outerMap
;
350 cpf
->summary
= summary
;
354 static WordFM
* dopy_InnerMap ( WordFM
* innerMap
)
356 return dopyFM ( innerMap
, NULL
,
357 (Word(*)(Word
))dopy_Counts
);
360 static void ddel_InnerMap ( WordFM
* innerMap
)
362 deleteFM( innerMap
, NULL
, (void(*)(Word
))ddel_Counts
);
365 static void ddel_CacheProfFile ( CacheProfFile
* cpf
)
368 if (cpf
->desc_lines
) {
369 for (p
= cpf
->desc_lines
; *p
; p
++)
371 free(cpf
->desc_lines
);
375 if (cpf
->events_line
)
376 free(cpf
->events_line
);
377 if (cpf
->summary_line
)
378 free(cpf
->summary_line
);
380 deleteFM( cpf
->outerMap
, (void(*)(Word
))ddel_FileFn
,
381 (void(*)(Word
))ddel_InnerMap
);
383 ddel_Counts(cpf
->summary
);
385 memset(cpf
, 0, sizeof(CacheProfFile
));
389 static void showCounts ( FILE* f
, Counts
* c
)
392 for (i
= 0; i
< c
->n_counts
; i
++) {
393 fprintf(f
, "%lld ", c
->counts
[i
]);
397 static void show_CacheProfFile ( FILE* f
, CacheProfFile
* cpf
)
406 for (d
= cpf
->desc_lines
; *d
; d
++)
407 fprintf(f
, "%s\n", *d
);
408 fprintf(f
, "%s\n", cpf
->cmd_line
);
409 fprintf(f
, "%s\n", cpf
->events_line
);
411 initIterFM( cpf
->outerMap
);
412 while (nextIterFM( cpf
->outerMap
, (Word
*)(&topKey
), (Word
*)(&topVal
) )) {
413 fprintf(f
, "fl=%s\nfn=%s\n",
414 topKey
->fi_name
, topKey
->fn_name
);
415 initIterFM( topVal
);
416 while (nextIterFM( topVal
, (Word
*)(&subKey
), (Word
*)(&subVal
) )) {
417 fprintf(f
, "%ld ", subKey
);
418 showCounts( f
, subVal
);
421 doneIterFM( topVal
);
423 doneIterFM( cpf
->outerMap
);
425 //fprintf(f, "%s\n", cpf->summary_line);
426 fprintf(f
, "summary:");
427 for (i
= 0; i
< cpf
->summary
->n_counts
; i
++)
428 fprintf(f
, " %lld", cpf
->summary
->counts
[i
]);
432 ////////////////////////////////////////////////////////////////
434 static Word
cmp_FileFn ( Word s1
, Word s2
)
436 FileFn
* ff1
= (FileFn
*)s1
;
437 FileFn
* ff2
= (FileFn
*)s2
;
438 Word r
= strcmp(ff1
->fi_name
, ff2
->fi_name
);
440 r
= strcmp(ff1
->fn_name
, ff2
->fn_name
);
444 static Word
cmp_unboxed_UWord ( Word s1
, Word s2
)
446 UWord u1
= (UWord
)s1
;
447 UWord u2
= (UWord
)s2
;
448 if (u1
< u2
) return -1;
449 if (u1
> u2
) return 1;
453 ////////////////////////////////////////////////////////////////
455 static Bool
parse_ULong ( /*OUT*/ULong
* res
, /*INOUT*/char** pptr
)
459 while (isspace(*ptr
)) ptr
++;
460 if (!isdigit(*ptr
)) {
462 return False
; /* end of string, or junk */
465 while (isdigit(*ptr
)) {
466 u64
= (u64
* 10) + (ULong
)(*ptr
- '0');
474 // str is a line of digits, starting with a line number. Parse it,
475 // returning the first number in *lnno and the rest in a newly
476 // allocated Counts struct. If lnno is non-NULL, treat the first
477 // number as a line number and assign it to *lnno instead of
478 // incorporating it in the counts array.
480 Counts
* splitUpCountsLine ( SOURCE
* s
, /*OUT*/UWord
* lnno
, char* str
)
488 ok
= parse_ULong( &tmpC
[n_tmpC
], &str
);
492 if (n_tmpC
>= N_TMPC
)
493 barf(s
, "N_TMPC too low. Increase and recompile.");
496 parseError(s
, "garbage in counts line");
497 if (lnno
? (n_tmpC
< 2) : (n_tmpC
< 1))
498 parseError(s
, "too few counts in count line");
501 *lnno
= (UWord
)tmpC
[0];
502 counts
= new_Counts( n_tmpC
-1, /*COPIED*/&tmpC
[1] );
504 counts
= new_Counts( n_tmpC
, /*COPIED*/&tmpC
[0] );
511 static void addCounts ( SOURCE
* s
, /*OUT*/Counts
* counts1
, Counts
* counts2
)
514 if (counts1
->n_counts
!= counts2
->n_counts
)
515 parseError(s
, "addCounts: inconsistent number of counts");
516 for (i
= 0; i
< counts1
->n_counts
; i
++)
517 counts1
->counts
[i
] += counts2
->counts
[i
];
520 static Bool
addCountsToMap ( SOURCE
* s
,
522 UWord lnno
, Counts
* newCounts
)
525 // look up lnno in the map. If none present, add a binding
526 // lnno->counts. If present, add counts to the existing entry.
527 if (lookupFM( counts_map
, (Word
*)(&oldCounts
), (Word
)lnno
)) {
528 // merge with existing binding
529 addCounts( s
, oldCounts
, newCounts
);
532 // create new binding
533 addToFM( counts_map
, (Word
)lnno
, (Word
)newCounts
);
539 void handle_counts ( SOURCE
* s
,
541 const char* fi
, const char* fn
, char* newCountsStr
)
549 if (0) printf("%s %s %s\n", fi
, fn
, newCountsStr
);
552 newCounts
= splitUpCountsLine( s
, &lnno
, newCountsStr
);
554 // Did we get the right number?
555 if (newCounts
->n_counts
!= cpf
->n_events
)
559 topKey
= malloc(sizeof(FileFn
));
561 topKey
->fi_name
= strdup(fi
);
562 topKey
->fn_name
= strdup(fn
);
564 if (! (topKey
&& topKey
->fi_name
&& topKey
->fn_name
))
565 mallocFail(s
, "handle_counts:");
568 if (lookupFM( cpf
->outerMap
, (Word
*)(&countsMap
), (Word
)topKey
)) {
569 // found it. Merge in new counts
570 freeNewCounts
= addCountsToMap( s
, countsMap
, lnno
, newCounts
);
573 // not found in the top map. Create new entry
574 countsMap
= newFM( malloc
, free
, cmp_unboxed_UWord
);
577 addToFM( cpf
->outerMap
, (Word
)topKey
, (Word
)countsMap
);
578 freeNewCounts
= addCountsToMap( s
, countsMap
, lnno
, newCounts
);
581 // also add to running summary total
582 addCounts( s
, cpf
->summary
, newCounts
);
584 // if safe to do so, free up the count vector
586 ddel_Counts(newCounts
);
591 parseError(s
, "# counts doesn't match # events");
595 /* Parse a complete file from the stream in 's'. If a parse error
596 happens, do not return; instead exit via parseError(). If an
597 out-of-memory condition happens, do not return; instead exit via
600 static CacheProfFile
* parse_CacheProfFile ( SOURCE
* s
)
602 #define M_TMP_DESCLINES 10
606 char* tmp_desclines
[M_TMP_DESCLINES
];
608 int n_tmp_desclines
= 0;
611 char* curr_fn
= strdup("???");
612 char* curr_fl
= strdup("???");
614 cpf
= new_CacheProfFile( NULL
, NULL
, NULL
, 0, NULL
, NULL
, NULL
);
616 mallocFail(s
, "parse_CacheProfFile(1)");
618 // Parse "desc:" lines
623 if (!streqn(line
, "desc: ", 6))
625 if (n_tmp_desclines
>= M_TMP_DESCLINES
)
626 barf(s
, "M_TMP_DESCLINES too low; increase and recompile");
627 tmp_desclines
[n_tmp_desclines
++] = strdup(line
);
630 if (n_tmp_desclines
== 0)
631 parseError(s
, "parse_CacheProfFile: no DESC lines present");
633 cpf
->desc_lines
= malloc( (1+n_tmp_desclines
) * sizeof(char*) );
634 if (cpf
->desc_lines
== NULL
)
635 mallocFail(s
, "parse_CacheProfFile(2)");
637 cpf
->desc_lines
[n_tmp_desclines
] = NULL
;
638 for (i
= 0; i
< n_tmp_desclines
; i
++)
639 cpf
->desc_lines
[i
] = tmp_desclines
[i
];
642 if (!streqn(line
, "cmd: ", 5))
643 parseError(s
, "parse_CacheProfFile: no CMD line present");
645 cpf
->cmd_line
= strdup(line
);
646 if (cpf
->cmd_line
== NULL
)
647 mallocFail(s
, "parse_CacheProfFile(3)");
649 // Parse "events:" line and figure out how many events there are
652 parseError(s
, "parse_CacheProfFile: eof before EVENTS line");
653 if (!streqn(line
, "events: ", 8))
654 parseError(s
, "parse_CacheProfFile: no EVENTS line present");
656 // figure out how many events there are by counting the number
657 // of space-alphanum transitions in the events_line
658 cpf
->events_line
= strdup(line
);
659 if (cpf
->events_line
== NULL
)
660 mallocFail(s
, "parse_CacheProfFile(3)");
663 assert(cpf
->events_line
[6] == ':');
664 for (p
= &cpf
->events_line
[6]; *p
; p
++) {
665 if (p
[0] == ' ' && isalpha(p
[1]))
669 // create the running cross-check summary
670 cpf
->summary
= new_Counts_Zeroed( cpf
->n_events
);
671 if (cpf
->summary
== NULL
)
672 mallocFail(s
, "parse_CacheProfFile(4)");
674 // create the outer map (file+fn name --> inner map)
675 cpf
->outerMap
= newFM ( malloc
, free
, cmp_FileFn
);
676 if (cpf
->outerMap
== NULL
)
677 mallocFail(s
, "parse_CacheProfFile(5)");
679 // process count lines
683 parseError(s
, "parse_CacheProfFile: eof before SUMMARY line");
685 if (isdigit(line
[0])) {
686 handle_counts(s
, cpf
, curr_fl
, curr_fn
, line
);
690 if (streqn(line
, "fn=", 3)) {
692 curr_fn
= strdup(line
+3);
696 if (streqn(line
, "fl=", 3)) {
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
;
751 #undef N_TMP_DESCLINES
755 static void merge_CacheProfInfo ( SOURCE
* s
,
756 /*MOD*/CacheProfFile
* dst
,
759 /* For each (filefn, innerMap) in src
761 add binding dopy(filefn)->dopy(innerMap) in src
763 // merge src->innerMap with dst->innerMap
764 for each (lineno, counts) in src->innerMap
765 if lineno not in dst->innerMap
766 add binding lineno->dopy(counts) to dst->innerMap
768 add counts into dst->innerMap[lineno]
770 /* Outer iterator: FileFn* -> WordFM* (inner iterator)
771 Inner iterator: UWord -> Counts*
780 /* First check mundane things: that the events: lines are
782 if (!streq( dst
->events_line
, src
->events_line
))
783 barf(s
, "\"events:\" line of most recent file does "
784 "not match those previously processed");
786 initIterFM( src
->outerMap
);
788 // for (filefn, innerMap) in src
789 while (nextIterFM( src
->outerMap
, (Word
*)&soKey
, (Word
*)&soVal
)) {
792 if (! lookupFM( dst
->outerMap
, (Word
*)&doVal
, (Word
)soKey
)) {
794 // no .. add dopy(filefn) -> dopy(innerMap) to src
795 FileFn
* c_soKey
= dopy_FileFn(soKey
);
796 WordFM
* c_soVal
= dopy_InnerMap(soVal
);
797 if ((!c_soKey
) || (!c_soVal
)) goto oom
;
798 addToFM( dst
->outerMap
, (Word
)c_soKey
, (Word
)c_soVal
);
802 // yes .. merge the two innermaps
805 // for (lno, counts) in soVal (source inner map)
806 while (nextIterFM( soVal
, (Word
*)&siKey
, (Word
*)&siVal
)) {
808 // is lno in the corresponding dst inner map?
809 if (! lookupFM( doVal
, (Word
*)&diVal
, siKey
)) {
811 // no .. add lineno->dopy(counts) to dst inner map
812 Counts
* c_siVal
= dopy_Counts( siVal
);
813 if (!c_siVal
) goto oom
;
814 addToFM( doVal
, siKey
, (Word
)c_siVal
);
818 // yes .. merge counts into dst inner map val
819 addCounts( s
, diVal
, siVal
);
828 // add the summaries too
829 addCounts(s
, dst
->summary
, src
->summary
);
834 mallocFail(s
, "merge_CacheProfInfo");
837 static void usage ( void )
839 fprintf(stderr
, "%s: Merges multiple cachegrind output files into one\n",
841 fprintf(stderr
, "%s: usage: %s [-o outfile] [files-to-merge]\n",
846 int main ( int argc
, char** argv
)
850 CacheProfFile
*cpf
, *cpfTmp
;
852 FILE* outfile
= NULL
;
853 char* outfilename
= NULL
;
862 for (i
= 1; i
< argc
; i
++) {
863 if (streq(argv
[i
], "-h") || streq(argv
[i
], "--help"))
867 /* Scan args, looking for '-o outfilename'. */
868 for (i
= 1; i
< argc
; i
++) {
869 if (streq(argv
[i
], "-o")) {
871 outfilename
= argv
[i
+1];
882 for (i
= 1; i
< argc
; i
++) {
884 if (i
== outfileix
) {
885 /* Skip '-o' and whatever follows it */
890 fprintf(stderr
, "%s: parsing %s\n", argv0
, argv
[i
]);
892 src
.filename
= argv
[i
];
893 src
.fp
= fopen(src
.filename
, "r");
896 barf(&src
, "Cannot open input file");
899 cpfTmp
= parse_CacheProfFile( &src
);
902 /* If this isn't the first file, merge */
904 /* this is the first file */
907 /* not the first file; merge */
908 fprintf(stderr
, "%s: merging %s\n", argv0
, argv
[i
]);
909 merge_CacheProfInfo( &src
, cpf
, cpfTmp
);
910 ddel_CacheProfFile( cpfTmp
);
915 /* Now create the output file. */
919 fprintf(stderr
, "%s: writing %s\n",
920 argv0
, outfilename
? outfilename
: "(stdout)" );
922 /* Write the output. */
924 outfile
= fopen(outfilename
, "w");
926 fprintf(stderr
, "%s: can't create output file %s\n",
935 show_CacheProfFile( outfile
, cpf
);
936 if (ferror(outfile
)) {
937 fprintf(stderr
, "%s: error writing output file %s\n",
938 argv0
, outfilename
? outfilename
: "(stdout)" );
940 if (outfile
!= stdout
)
946 if (outfile
!= stdout
)
949 ddel_CacheProfFile( cpf
);
956 //------------------------------------------------------------------//
958 //--- Implementation ---//
959 //------------------------------------------------------------------//
961 /* ------------ Implementation ------------ */
963 /* One element of the AVL tree */
968 struct _AvlNode
* left
;
969 struct _AvlNode
* right
;
981 #define WFM_STKMAX 32 // At most 2**32 entries can be iterated over
985 void* (*alloc_nofail
)( SizeT
);
986 void (*dealloc
)(void*);
987 Word (*kCmp
)(Word
,Word
);
988 AvlNode
* nodeStack
[WFM_STKMAX
]; // Iterator node stack
989 Int numStack
[WFM_STKMAX
]; // Iterator num stack
990 Int stackTop
; // Iterator stack pointer, one past end
994 static Bool
avl_removeroot_wrk(AvlNode
** t
, Word(*kCmp
)(Word
,Word
));
996 /* Swing to the left. Warning: no balance maintainance. */
997 static void avl_swl ( AvlNode
** root
)
1000 AvlNode
* b
= a
->right
;
1006 /* Swing to the right. Warning: no balance maintainance. */
1007 static void avl_swr ( AvlNode
** root
)
1010 AvlNode
* b
= a
->left
;
1016 /* Balance maintainance after especially nasty swings. */
1017 static void avl_nasty ( AvlNode
* root
)
1019 switch (root
->balance
) {
1021 root
->left
->balance
= 0;
1022 root
->right
->balance
= 1;
1025 root
->left
->balance
= -1;
1026 root
->right
->balance
= 0;
1029 root
->left
->balance
= 0;
1030 root
->right
->balance
= 0;
1038 /* Find size of a non-NULL tree. */
1039 static Word
size_avl_nonNull ( AvlNode
* nd
)
1041 return 1 + (nd
->left
? size_avl_nonNull(nd
->left
) : 0)
1042 + (nd
->right
? size_avl_nonNull(nd
->right
) : 0);
1045 /* Insert element a into the AVL tree t. Returns True if the depth of
1046 the tree has grown. If element with that key is already present,
1047 just copy a->val to existing node, first returning old ->val field
1048 of existing node in *oldV, so that the caller can finalize it
1052 Bool
avl_insert_wrk ( AvlNode
** rootp
,
1053 /*OUT*/MaybeWord
* oldV
,
1055 Word (*kCmp
)(Word
,Word
) )
1065 /* insert into an empty tree? */
1071 cmpres
= kCmp( (*rootp
)->key
, a
->key
);
1074 /* insert into the left subtree */
1075 if ((*rootp
)->left
) {
1076 AvlNode
* left_subtree
= (*rootp
)->left
;
1077 if (avl_insert_wrk(&left_subtree
, oldV
, a
, kCmp
)) {
1078 switch ((*rootp
)->balance
--) {
1079 case 1: return False
;
1080 case 0: return True
;
1084 if ((*rootp
)->left
->balance
< 0) {
1086 (*rootp
)->balance
= 0;
1087 (*rootp
)->right
->balance
= 0;
1089 avl_swl( &((*rootp
)->left
) );
1091 avl_nasty( *rootp
);
1094 (*rootp
)->left
= left_subtree
;
1099 if ((*rootp
)->balance
--)
1103 assert(0);/*NOTREACHED*/
1107 /* insert into the right subtree */
1108 if ((*rootp
)->right
) {
1109 AvlNode
* right_subtree
= (*rootp
)->right
;
1110 if (avl_insert_wrk(&right_subtree
, oldV
, a
, kCmp
)) {
1111 switch((*rootp
)->balance
++){
1112 case -1: return False
;
1113 case 0: return True
;
1117 if ((*rootp
)->right
->balance
> 0) {
1119 (*rootp
)->balance
= 0;
1120 (*rootp
)->left
->balance
= 0;
1122 avl_swr( &((*rootp
)->right
) );
1124 avl_nasty( *rootp
);
1127 (*rootp
)->right
= right_subtree
;
1131 (*rootp
)->right
= a
;
1132 if ((*rootp
)->balance
++)
1136 assert(0);/*NOTREACHED*/
1139 /* cmpres == 0, a duplicate - replace the val, but don't
1140 incorporate the node in the tree */
1142 oldV
->w
= (*rootp
)->val
;
1143 (*rootp
)->val
= a
->val
;
1148 /* Remove an element a from the AVL tree t. a must be part of
1149 the tree. Returns True if the depth of the tree has shrunk.
1152 Bool
avl_remove_wrk ( AvlNode
** rootp
,
1154 Word(*kCmp
)(Word
,Word
) )
1157 Word cmpres
= kCmp( (*rootp
)->key
, a
->key
);
1160 /* remove from the left subtree */
1161 AvlNode
* left_subtree
= (*rootp
)->left
;
1162 assert(left_subtree
);
1163 ch
= avl_remove_wrk(&left_subtree
, a
, kCmp
);
1164 (*rootp
)->left
=left_subtree
;
1166 switch ((*rootp
)->balance
++) {
1167 case -1: return True
;
1168 case 0: return False
;
1172 switch ((*rootp
)->right
->balance
) {
1175 (*rootp
)->balance
= -1;
1176 (*rootp
)->left
->balance
= 1;
1180 (*rootp
)->balance
= 0;
1181 (*rootp
)->left
->balance
= 0;
1188 avl_swr( &((*rootp
)->right
) );
1190 avl_nasty( *rootp
);
1196 /* remove from the right subtree */
1197 AvlNode
* right_subtree
= (*rootp
)->right
;
1198 assert(right_subtree
);
1199 ch
= avl_remove_wrk(&right_subtree
, a
, kCmp
);
1200 (*rootp
)->right
= right_subtree
;
1202 switch ((*rootp
)->balance
--) {
1203 case 1: return True
;
1204 case 0: return False
;
1208 switch ((*rootp
)->left
->balance
) {
1211 (*rootp
)->balance
= 1;
1212 (*rootp
)->right
->balance
= -1;
1216 (*rootp
)->balance
= 0;
1217 (*rootp
)->right
->balance
= 0;
1224 avl_swl( &((*rootp
)->left
) );
1226 avl_nasty( *rootp
);
1231 assert(cmpres
== 0);
1232 assert((*rootp
)==a
);
1233 return avl_removeroot_wrk(rootp
, kCmp
);
1238 /* Remove the root of the AVL tree *rootp.
1239 * Warning: dumps core if *rootp is empty
1242 Bool
avl_removeroot_wrk ( AvlNode
** rootp
,
1243 Word(*kCmp
)(Word
,Word
) )
1247 if (!(*rootp
)->left
) {
1248 if (!(*rootp
)->right
) {
1252 (*rootp
) = (*rootp
)->right
;
1255 if (!(*rootp
)->right
) {
1256 (*rootp
) = (*rootp
)->left
;
1259 if ((*rootp
)->balance
< 0) {
1260 /* remove from the left subtree */
1262 while (a
->right
) a
= a
->right
;
1264 /* remove from the right subtree */
1265 a
= (*rootp
)->right
;
1266 while (a
->left
) a
= a
->left
;
1268 ch
= avl_remove_wrk(rootp
, a
, kCmp
);
1269 a
->left
= (*rootp
)->left
;
1270 a
->right
= (*rootp
)->right
;
1271 a
->balance
= (*rootp
)->balance
;
1273 if(a
->balance
== 0) return ch
;
1278 AvlNode
* avl_find_node ( AvlNode
* t
, Word k
, Word(*kCmp
)(Word
,Word
) )
1282 if (t
== NULL
) return NULL
;
1283 cmpres
= kCmp(t
->key
, k
);
1284 if (cmpres
> 0) t
= t
->left
; else
1285 if (cmpres
< 0) t
= t
->right
; else
1290 // Clear the iterator stack.
1291 static void stackClear(WordFM
* fm
)
1295 for (i
= 0; i
< WFM_STKMAX
; i
++) {
1296 fm
->nodeStack
[i
] = NULL
;
1297 fm
->numStack
[i
] = 0;
1302 // Push onto the iterator stack.
1303 static inline void stackPush(WordFM
* fm
, AvlNode
* n
, Int i
)
1305 assert(fm
->stackTop
< WFM_STKMAX
);
1306 assert(1 <= i
&& i
<= 3);
1307 fm
->nodeStack
[fm
->stackTop
] = n
;
1308 fm
-> numStack
[fm
->stackTop
] = i
;
1312 // Pop from the iterator stack.
1313 static inline Bool
stackPop(WordFM
* fm
, AvlNode
** n
, Int
* i
)
1315 assert(fm
->stackTop
<= WFM_STKMAX
);
1317 if (fm
->stackTop
> 0) {
1319 *n
= fm
->nodeStack
[fm
->stackTop
];
1320 *i
= fm
-> numStack
[fm
->stackTop
];
1321 assert(1 <= *i
&& *i
<= 3);
1322 fm
->nodeStack
[fm
->stackTop
] = NULL
;
1323 fm
-> numStack
[fm
->stackTop
] = 0;
1331 AvlNode
* avl_dopy ( AvlNode
* nd
,
1334 void*(alloc_nofail
)(SizeT
) )
1339 nyu
= alloc_nofail(sizeof(AvlNode
));
1342 nyu
->left
= nd
->left
;
1343 nyu
->right
= nd
->right
;
1344 nyu
->balance
= nd
->balance
;
1348 nyu
->key
= dopyK( nd
->key
);
1349 if (nd
->key
!= 0 && nyu
->key
== 0)
1350 return NULL
; /* oom in key dcopy */
1352 /* copying assumedly unboxed keys */
1358 nyu
->val
= dopyV( nd
->val
);
1359 if (nd
->val
!= 0 && nyu
->val
== 0)
1360 return NULL
; /* oom in val dcopy */
1362 /* copying assumedly unboxed vals */
1368 nyu
->left
= avl_dopy( nyu
->left
, dopyK
, dopyV
, alloc_nofail
);
1373 nyu
->right
= avl_dopy( nyu
->right
, dopyK
, dopyV
, alloc_nofail
);
1381 /* --- Public interface functions --- */
1383 /* Initialise a WordFM. */
1384 void initFM ( WordFM
* fm
,
1385 void* (*alloc_nofail
)( SizeT
),
1386 void (*dealloc
)(void*),
1387 Word (*kCmp
)(Word
,Word
) )
1391 fm
->alloc_nofail
= alloc_nofail
;
1392 fm
->dealloc
= dealloc
;
1396 /* Allocate and Initialise a WordFM. */
1397 WordFM
* newFM( void* (*alloc_nofail
)( SizeT
),
1398 void (*dealloc
)(void*),
1399 Word (*kCmp
)(Word
,Word
) )
1401 WordFM
* fm
= alloc_nofail(sizeof(WordFM
));
1403 initFM(fm
, alloc_nofail
, dealloc
, kCmp
);
1407 static void avl_free ( AvlNode
* nd
,
1410 void(*dealloc
)(void*) )
1415 avl_free(nd
->left
, kFin
, vFin
, dealloc
);
1417 avl_free(nd
->right
, kFin
, vFin
, dealloc
);
1422 memset(nd
, 0, sizeof(AvlNode
));
1426 /* Free up the FM. If kFin is non-NULL, it is applied to keys
1427 before the FM is deleted; ditto with vFin for vals. */
1428 void deleteFM ( WordFM
* fm
, void(*kFin
)(Word
), void(*vFin
)(Word
) )
1430 void(*dealloc
)(void*) = fm
->dealloc
;
1431 avl_free( fm
->root
, kFin
, vFin
, dealloc
);
1432 memset(fm
, 0, sizeof(WordFM
) );
1436 /* Add (k,v) to fm. */
1437 void addToFM ( WordFM
* fm
, Word k
, Word v
)
1441 node
= fm
->alloc_nofail( sizeof(struct _AvlNode
) );
1446 avl_insert_wrk( &fm
->root
, &oldV
, node
, fm
->kCmp
);
1447 //if (oldV.b && fm->vFin)
1448 // fm->vFin( oldV.w );
1453 // Delete key from fm, returning associated val if found
1454 Bool
delFromFM ( WordFM
* fm
, /*OUT*/Word
* oldV
, Word key
)
1456 AvlNode
* node
= avl_find_node( fm
->root
, key
, fm
->kCmp
);
1458 avl_remove_wrk( &fm
->root
, node
, fm
->kCmp
);
1468 // Look up in fm, assigning found val at spec'd address
1469 Bool
lookupFM ( WordFM
* fm
, /*OUT*/Word
* valP
, Word key
)
1471 AvlNode
* node
= avl_find_node( fm
->root
, key
, fm
->kCmp
);
1481 Word
sizeFM ( WordFM
* fm
)
1483 // Hmm, this is a bad way to do this
1484 return fm
->root
? size_avl_nonNull( fm
->root
) : 0;
1487 // set up FM for iteration
1488 void initIterFM ( WordFM
* fm
)
1493 stackPush(fm
, fm
->root
, 1);
1496 // get next key/val pair. Will assert if fm has been modified
1497 // or looked up in since initIterFM was called.
1498 Bool
nextIterFM ( WordFM
* fm
, /*OUT*/Word
* pKey
, /*OUT*/Word
* pVal
)
1505 // This in-order traversal requires each node to be pushed and popped
1506 // three times. These could be avoided by updating nodes in-situ on the
1507 // top of the stack, but the push/pop cost is so small that it's worth
1508 // keeping this loop in this simpler form.
1509 while (stackPop(fm
, &n
, &i
)) {
1512 stackPush(fm
, n
, 2);
1513 if (n
->left
) stackPush(fm
, n
->left
, 1);
1516 stackPush(fm
, n
, 3);
1517 if (pKey
) *pKey
= n
->key
;
1518 if (pVal
) *pVal
= n
->val
;
1521 if (n
->right
) stackPush(fm
, n
->right
, 1);
1528 // Stack empty, iterator is exhausted, return NULL
1532 // clear the I'm iterating flag
1533 void doneIterFM ( WordFM
* fm
)
1537 WordFM
* dopyFM ( WordFM
* fm
, Word(*dopyK
)(Word
), Word(*dopyV
)(Word
) )
1541 /* can't clone the fm whilst iterating on it */
1542 assert(fm
->stackTop
== 0);
1544 nyu
= fm
->alloc_nofail( sizeof(WordFM
) );
1550 memset(fm
->nodeStack
, 0, sizeof(fm
->nodeStack
));
1551 memset(fm
->numStack
, 0, sizeof(fm
->numStack
));
1554 nyu
->root
= avl_dopy( nyu
->root
, dopyK
, dopyV
, fm
->alloc_nofail
);
1562 //------------------------------------------------------------------//
1563 //--- end WordFM ---//
1564 //--- Implementation ---//
1565 //------------------------------------------------------------------//
1567 /*--------------------------------------------------------------------*/
1568 /*--- end cg_merge.c ---*/
1569 /*--------------------------------------------------------------------*/