diff --git a/libdatrie/ChangeLog b/libdatrie/ChangeLog index b37fa3d..5eba834 100644 --- a/libdatrie/ChangeLog +++ b/libdatrie/ChangeLog @@ -1,3 +1,18 @@ +2015-04-21 Theppitak Karoonboonyanan + + Fix infinite loop on empty trie iteration. + + * tests/Makefile.am, +tests/test_null_trie.c: + - Add test case for empty trie iteration. + + * datrie/darray.c (da_first_separate): + - Fix error condition after loop ending. + + Thanks Sergei Lebedev for the report + via personal mail. + + Original report: https://github.com/kmike/datrie/issues/17 + 2014-01-10 Theppitak Karoonboonyanan * configure.ac: @@ -1365,7 +1380,7 @@ 2006-08-18 Theppitak Karoonboonyanan * datrie/trie.c (trie_retrieve, trie_store, trie_delete): Always walk - the null-terminator in tail. Otherwise, comparison with shorter key + the null-terminator in tail. Otherwise, comparison with shorter key will terminate at separate node. 2006-08-18 Theppitak Karoonboonyanan @@ -1380,7 +1395,7 @@ key data. 2006-08-17 Theppitak Karoonboonyanan - + * configure.ac, Makefile.am, +doc/Makefile.am, +doc/Doxyfile.in: Generated document using doxygen. @@ -1415,7 +1430,7 @@ 2006-08-17 Theppitak Karoonboonyanan - * datrie/darray.c (da_find_free_base): Made sure the free cell for + * datrie/darray.c (da_find_free_base): Made sure the free cell for first symbol is beyond header cells. Also repeatedly extended the pool until a free cell is found, in case free list is restarted. @@ -1441,7 +1456,7 @@ * datrie/darray.c (da_get_free_list): Fixed typo in macro name. - * datrie/datrie.c (da_extend_pool): Updated num_cells immediately + * datrie/datrie.c (da_extend_pool): Updated num_cells immediately after realloc(), to let the cell accesses pass boundary checks. * datrie/tail.c (tail_get_suffix, tail_set_suffix, tail_alloc_block, @@ -1479,7 +1494,7 @@ 2006-08-14 Theppitak Karoonboonyanan * datrie/Makefile.am, +datrie/darray.c, +datrie/fileutils.h: - Added darray.c for double-array structure implementation, and + Added darray.c for double-array structure implementation, and fileutils.h declarations for keeping file manipulation functions. * datrie/triedefs.h: Added TRIE_CHAR_MAX constant for alphabet diff --git a/libdatrie/datrie/darray.c b/libdatrie/datrie/darray.c index c07712a..f16fcd0 100644 --- a/libdatrie/datrie/darray.c +++ b/libdatrie/datrie/darray.c @@ -774,7 +774,7 @@ da_first_separate (DArray *d, TrieIndex root, TrieString *keybuff) break; } - if (c == max_c) + if (c > max_c) return TRIE_INDEX_ERROR; trie_string_append_char (keybuff, c); diff --git a/libdatrie/tests/Makefile.am b/libdatrie/tests/Makefile.am index 9720c3d..1f899cb 100644 --- a/libdatrie/tests/Makefile.am +++ b/libdatrie/tests/Makefile.am @@ -8,6 +8,7 @@ TESTS = \ test_store-retrieve \ test_file \ test_nonalpha \ + test_null_trie \ $(NULL) check_PROGRAMS = \ @@ -16,6 +17,7 @@ check_PROGRAMS = \ test_store-retrieve \ test_file \ test_nonalpha \ + test_null_trie \ $(NULL) noinst_HEADERS = \ @@ -52,3 +54,8 @@ test_nonalpha_SOURCES = \ $(NULL) test_nonalpha_LDADD = $(top_builddir)/datrie/libdatrie.la +test_null_trie_SOURCES = \ + test_null_trie.c \ + utils.c \ + $(NULL) +test_null_trie_LDADD = $(top_builddir)/datrie/libdatrie.la diff --git a/libdatrie/tests/test_null_trie.c b/libdatrie/tests/test_null_trie.c new file mode 100644 index 0000000..ae21ffc --- /dev/null +++ b/libdatrie/tests/test_null_trie.c @@ -0,0 +1,97 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * libdatrie - Double-Array Trie Library + * Copyright (C) 2015 Theppitak Karoonboonyanan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * test_null_trie.c - Test for datrie iteration on empty trie + * Created: 2015-04-21 + * Author: Theppitak Karoonboonyanan + */ + +#include +#include "utils.h" +#include +#include + +int +main () +{ + Trie *test_trie; + TrieState *trie_root_state; + TrieIterator *trie_it; + Bool is_failed; + + msg_step ("Preparing empty trie"); + test_trie = en_trie_new (); + if (!test_trie) { + fprintf (stderr, "Fail to create test trie\n"); + goto err_trie_not_created; + } + + /* iterate & check */ + msg_step ("Iterating"); + trie_root_state = trie_root (test_trie); + if (!trie_root_state) { + printf ("Failed to get trie root state\n"); + goto err_trie_created; + } + trie_it = trie_iterator_new (trie_root_state); + if (!trie_it) { + printf ("Failed to get trie iterator\n"); + goto err_trie_root_created; + } + + is_failed = FALSE; + while (trie_iterator_next (trie_it)) { + AlphaChar *key; + + printf ("Got entry from empty trie, which is weird!\n"); + + key = trie_iterator_get_key (trie_it); + if (key) { + printf ("Got key from empty trie, which is weird! (key='%ls')\n", + key); + is_failed = TRUE; + free (key); + } + } + + if (is_failed) { + printf ("Errors found in empty trie iteration.\n"); + goto err_trie_it_created; + } + + trie_iterator_free (trie_it); + trie_state_free (trie_root_state); + trie_free (test_trie); + return 0; + +err_trie_it_created: + trie_iterator_free (trie_it); +err_trie_root_created: + trie_state_free (trie_root_state); +err_trie_created: + trie_free (test_trie); +err_trie_not_created: + return 1; +} + +/* +vi:ts=4:ai:expandtab +*/ diff --git a/tests/test_iteration.py b/tests/test_iteration.py index ac86875..92ed5cb 100644 --- a/tests/test_iteration.py +++ b/tests/test_iteration.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, unicode_literals + import string import datrie -WORDS = ['producers', 'pool', 'prepare', 'preview', 'prize', 'produce', 'producer', 'progress'] +WORDS = ['producers', 'pool', 'prepare', 'preview', 'prize', 'produce', + 'producer', 'progress'] + def _trie(): trie = datrie.Trie(ranges=[(chr(0), chr(127))]) @@ -30,6 +33,7 @@ def test_base_trie_data(): it.next() assert it.data() == 2 + def test_next(): trie = _trie() state = datrie.State(trie) @@ -56,6 +60,7 @@ def test_next_non_root(): assert len(values) == 7 assert values == [3, 4, 5, 6, 7, 1, 8] + def test_next_tail(): trie = _trie() state = datrie.State(trie) @@ -69,7 +74,6 @@ def test_next_tail(): assert values == [2] - def test_keys(): trie = _trie() state = datrie.State(trie) @@ -94,6 +98,7 @@ def test_keys_non_root(): assert keys == ['duce', 'ducer', 'ducers', 'gress'] + def test_keys_tail(): trie = _trie() state = datrie.State(trie) @@ -105,3 +110,11 @@ def test_keys_tail(): keys.append(it.key()) assert keys == ['duce', 'ducer', 'ducers', 'gress'] + + +def test_len(): + trie = datrie.Trie(ranges=[(chr(0), chr(127))]) + # Calling len on an empty trie caused segfault, see #17 on GitHub. + assert len(trie) == 0 + trie['producer'] = 42 + assert len(trie) == 1