diff --git a/ChangeLog b/ChangeLog index 2b6b146..86ac043 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,20 @@ 2002-11-10 Bruno Haible + Fix memory leaks. + * src/keyword.h (empty_string): New declaration. + * src/keyword.cc (empty_string): New variable. + * src/input.h (Input::_input): Make public. + (Input::_input_end): New field. + * src/input.cc (read_input): When removing leading whitespace from + struct_decl, reallocate it. For rest, use empty_string instead of "". + Set _input_end. + (Input::~Input): Delete _struct_decl, _struct_tag, _return_type. + * src/search.cc (Search::prepare): When removing an element from + the keyword list, delete the list node. + (Search::~Search): Delete _occurrences, _asso_values. + * src/main.cc (main): Between Search::~Search and Input::~Input, + destroy the keyword list. + Rewrite the input routines. * src/input.h: Don't include read-line.h. (Input): Don't inherit from class Read_Line. diff --git a/src/input.cc b/src/input.cc index 43e502e..a04856b 100644 --- a/src/input.cc +++ b/src/input.cc @@ -299,9 +299,19 @@ Input::read_input () if (struct_decl) { /* Drop leading whitespace. */ - while (struct_decl[0] == '\n' || struct_decl[0] == ' ' - || struct_decl[0] == '\t') - struct_decl++; + { + char *p = struct_decl; + while (p[0] == '\n' || p[0] == ' ' || p[0] == '\t') + p++; + if (p != struct_decl) + { + size_t len = strlen (p); + char *new_struct_decl = new char[len + 1]; + memcpy (new_struct_decl, p, len + 1); + delete[] struct_decl; + struct_decl = new_struct_decl; + } + } /* Drop trailing whitespace. */ for (char *p = struct_decl + strlen (struct_decl); p > struct_decl;) if (p[-1] == '\n' || p[-1] == ' ' || p[-1] == '\t') @@ -315,20 +325,19 @@ Input::read_input () " for option --struct-type\n"); exit (1); } - if (struct_decl) - { - /* Ensure trailing semicolon. */ - size_t old_len = strlen (struct_decl); - if (struct_decl[old_len - 1] != ';') - { - char *new_struct_decl = new char[old_len + 2]; - memcpy (new_struct_decl, struct_decl, old_len); - new_struct_decl[old_len] = ';'; - new_struct_decl[old_len + 1] = '\0'; - delete[] struct_decl; - struct_decl = new_struct_decl; - } - } + { + /* Ensure trailing semicolon. */ + size_t old_len = strlen (struct_decl); + if (struct_decl[old_len - 1] != ';') + { + char *new_struct_decl = new char[old_len + 2]; + memcpy (new_struct_decl, struct_decl, old_len); + new_struct_decl[old_len] = ';'; + new_struct_decl[old_len + 1] = '\0'; + delete[] struct_decl; + struct_decl = new_struct_decl; + } + } /* Set _struct_decl to the entire declaration. */ _struct_decl = struct_decl; /* Set _struct_tag to the naked "struct something". */ @@ -529,7 +538,7 @@ Input::read_input () rest = line_rest; } else - rest = ""; + rest = empty_string; } else { @@ -541,7 +550,7 @@ Input::read_input () { keyword = line; keyword_length = lp - line; - rest = ""; + rest = empty_string; break; } if (strchr (delimiters, *lp) != NULL) @@ -560,7 +569,7 @@ Input::read_input () rest = line_rest; } else - rest = ""; + rest = empty_string; break; } lp++; @@ -588,9 +597,14 @@ Input::read_input () /* To be freed in the destructor. */ _input = input; + _input_end = input_end; } Input::~Input () { + /* Free allocated memory. */ + delete[] _return_type; + delete[] _struct_tag; + delete[] _struct_decl; delete[] _input; } diff --git a/src/input.h b/src/input.h index 947fd5f..6d6fd87 100644 --- a/src/input.h +++ b/src/input.h @@ -40,9 +40,10 @@ private: FILE * _stream; /* Creates the keywords. */ Keyword_Factory * const _factory; +public: /* Memory block containing the entire input. */ char * _input; -public: + char * _input_end; /* The C code from the declarations section. */ const char * _verbatim_declarations; const char * _verbatim_declarations_end; diff --git a/src/keyword.cc b/src/keyword.cc index a839e42..6250165 100644 --- a/src/keyword.cc +++ b/src/keyword.cc @@ -121,6 +121,11 @@ Keyword_Factory::~Keyword_Factory () } +/* ------------------------------------------------------------------------- */ + +char empty_string[1] = ""; + + #ifndef __OPTIMIZE__ #define INLINE /* not inline */ diff --git a/src/keyword.h b/src/keyword.h index cc754ba..0d1eec9 100644 --- a/src/keyword.h +++ b/src/keyword.h @@ -86,11 +86,14 @@ public: virtual ~Keyword_Factory (); /* Creates a new Keyword. */ - virtual /*abstract */ Keyword * + virtual /*abstract*/ Keyword * create_keyword (const char *allchars, int allchars_length, const char *rest) = 0; }; +/* A statically allocated empty string. */ +extern char empty_string[1]; + #ifdef __OPTIMIZE__ #define INLINE inline diff --git a/src/main.cc b/src/main.cc index 0c03bfe..18ee0bf 100644 --- a/src/main.cc +++ b/src/main.cc @@ -49,6 +49,8 @@ KeywordExt_Factory::create_keyword (const char *allchars, int allchars_length, c int main (int argc, char *argv[]) { + int exitcode; + /* Set the Options. Open the input file and assign stdin to it. */ option.parse_options (argc, argv); @@ -61,46 +63,79 @@ main (int argc, char *argv[]) exit (1); } - /* Initialize the keyword list. */ - KeywordExt_Factory factory; - Input inputter (stdin, &factory); - inputter.read_input (); - /* We can cast the keyword list to KeywordExt_List* because its list - elements were created by KeywordExt_Factory. */ - KeywordExt_List* list = static_cast(inputter._head); + { + /* Initialize the keyword list. */ + KeywordExt_Factory factory; + Input inputter (stdin, &factory); + inputter.read_input (); + /* We can cast the keyword list to KeywordExt_List* because its list + elements were created by KeywordExt_Factory. */ + KeywordExt_List* list = static_cast(inputter._head); - /* Search for a good hash function. */ - Search searcher (list); - searcher.optimize (); - - /* Output the hash function code. */ - Output outputter (searcher._head, - inputter._struct_decl, - inputter._return_type, - inputter._struct_tag, - inputter._verbatim_declarations, - inputter._verbatim_declarations_end, - inputter._verbatim_declarations_lineno, - inputter._verbatim_code, - inputter._verbatim_code_end, - inputter._verbatim_code_lineno, - searcher._total_keys, - searcher._total_duplicates, - searcher._max_key_len, - searcher._min_key_len, - searcher._alpha_size, - searcher._occurrences, - searcher._asso_values); - outputter.output (); - - /* Check for write error on stdout. */ - int status = 0; - if (fflush (stdout) || ferror (stdout)) { - fprintf (stderr, "error while writing output file\n"); - status = 1; + /* Search for a good hash function. */ + Search searcher (list); + searcher.optimize (); + list = searcher._head; + + { + /* Output the hash function code. */ + Output outputter (searcher._head, + inputter._struct_decl, + inputter._return_type, + inputter._struct_tag, + inputter._verbatim_declarations, + inputter._verbatim_declarations_end, + inputter._verbatim_declarations_lineno, + inputter._verbatim_code, + inputter._verbatim_code_end, + inputter._verbatim_code_lineno, + searcher._total_keys, + searcher._total_duplicates, + searcher._max_key_len, + searcher._min_key_len, + searcher._alpha_size, + searcher._occurrences, + searcher._asso_values); + outputter.output (); + + /* Check for write error on stdout. */ + exitcode = 0; + if (fflush (stdout) || ferror (stdout)) + { + fprintf (stderr, "error while writing output file\n"); + exitcode = 1; + } + + /* Here we run the Output destructor. */ + } + /* Here we run the Search destructor. */ } + /* Also delete the list that was allocated inside Input and reordered + inside Search. */ + for (KeywordExt_List *ptr = list; ptr; ptr = ptr->rest()) + { + KeywordExt *keyword = ptr->first(); + do + { + KeywordExt *next_keyword = keyword->_duplicate_link; + delete[] keyword->_selchars; + if (keyword->_rest != empty_string) + delete[] keyword->_rest; + if (!(keyword->_allchars >= inputter._input + && keyword->_allchars < inputter._input_end)) + delete[] keyword->_allchars; + delete keyword; + keyword = next_keyword; + } + while (keyword != NULL); + } + delete_list (list); + + /* Here we run the Input destructor. */ + } + /* Don't use exit() here, it skips the destructors. */ - return status; + return exitcode; } diff --git a/src/search.cc b/src/search.cc index 8e04d59..a17cc28 100644 --- a/src/search.cc +++ b/src/search.cc @@ -94,10 +94,11 @@ Search::prepare () Hash_Table representatives (_list_len, option[NOLENGTH]); KeywordExt_List *prev = NULL; /* list node before temp */ - for (temp = _head; temp; temp = temp->rest()) + for (temp = _head; temp; ) { KeywordExt *keyword = temp->first(); KeywordExt *other_keyword = representatives.insert (keyword); + KeywordExt_List *garbage = NULL; if (other_keyword) { @@ -105,6 +106,7 @@ Search::prepare () _list_len--; /* Remove keyword from the main list. */ prev->rest() = temp->rest(); + garbage = temp; /* And insert it on other_keyword's duplicate list. */ keyword->_duplicate_link = other_keyword->_duplicate_link; other_keyword->_duplicate_link = keyword; @@ -121,6 +123,9 @@ Search::prepare () keyword->_duplicate_link = NULL; prev = temp; } + temp = temp->rest(); + if (garbage) + delete garbage; } } @@ -798,4 +803,6 @@ Search::~Search () fprintf (stderr, "End dumping list.\n\n"); } + delete[] _asso_values; + delete[] _occurrences; } diff --git a/tests/Makefile.in b/tests/Makefile.in index 56c7058..e78fafc 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -48,6 +48,7 @@ SHELL = /bin/sh VPATH = $(srcdir) GPERF = ../src/gperf +#GPERF = valgrind --num-callers=20 --leak-check=yes --leak-resolution=high --show-reachable=yes ../src/gperf all :