From 1a3735251df887bbdab7dae410ed9a4ff8432b1e Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Tue, 8 Sep 2015 09:14:39 -0400 Subject: [PATCH] core/string: Move string_lowercase() out of "string" namespace And refactor to use glib string functions while we're at it. Signed-off-by: Anna Schumaker --- core/filter.cpp | 9 ++++-- core/string.cpp | 66 +++++++++++++++---------------------------- core/tags/generic.cpp | 11 ++++++-- include/core/string.h | 34 ++++++---------------- tests/core/string.cpp | 24 ++++++++-------- 5 files changed, 59 insertions(+), 85 deletions(-) diff --git a/core/filter.cpp b/core/filter.cpp index ccc460b2..eae4b4f2 100644 --- a/core/filter.cpp +++ b/core/filter.cpp @@ -12,9 +12,12 @@ static Index filter_index("", false); const std::string filter :: add(const std::string &text, unsigned int index) { - const std::string lc = string :: lowercase(text); + gchar *g_lc = string_lowercase(text.c_str()); + const std::string lc = g_lc; size_t begin = 0, end; + g_free(g_lc); + for (end = 1; end <= lc.size(); end++) { filter_index.insert(lc.substr(begin, end - begin), index); if (lc[end] == ' ') @@ -35,10 +38,12 @@ static void do_set_intersection(IndexEntry *entry, void filter :: search(const std::string &text, std::set &res) { - const std::string lc = string :: lowercase(text); + gchar *g_lc = string_lowercase(text.c_str()); + const std::string lc = g_lc; size_t begin = 0, end; IndexEntry *found; + g_free(g_lc); res.clear(); for (end = 1; end <= lc.size(); end++) { diff --git a/core/string.cpp b/core/string.cpp index 6ecf50a2..69a229dd 100644 --- a/core/string.cpp +++ b/core/string.cpp @@ -1,7 +1,8 @@ -/** +/* * Copyright 2015 (c) Anna Schumaker. */ #include +#include #define O_SECONDS (1) #define O_MINUTES (60) @@ -39,51 +40,28 @@ gchar *string_sec2str_long(unsigned int sec) return res; } -static char _to_lower(char c) +static void _drop_special(gchar *str) { - if ((c >= 'a') && (c <= 'z')) - return c; - if ((c >= '0') && (c <= '9')) - return c; - if ((c >= 'A') && (c <= 'Z')) - return c + ('a' - 'A'); + unsigned int i, j = 0; - switch (c) { - case '\\': - case '/': - case ',': - case ';': - case '(': - case ')': - case '_': - case '-': - case '~': - case '+': - case '"': - case ' ': - case ' ': - return ' '; - default: - return 0; - } -} - -const std::string string :: lowercase(const std::string &str) -{ - std::string ret; - char c, last = ' '; - - for (unsigned int i = 0; i < str.size(); i++) { - c = _to_lower(str[i]); - - if (c > 0) { - if ((c != ' ') || (last != ' ')) - ret += c; - last = c; - } + for (i = 0; i < strlen(str); i++) { + if (g_ascii_isalnum(str[i])) + str[j++] = str[i]; + else if ((j > 0) && (str[i] == ' ') && (str[j - 1] != ' ')) + str[j++] = str[i]; } - if (ret[ret.size() - 1] == ' ') - return ret.substr(0, ret.size() - 1); - return ret; + while (j < i) + str[j++] = '\0'; +} + +gchar *string_lowercase(const gchar *str) +{ + gchar *res = g_ascii_strdown(str, -1); + + g_strdelimit(res, "\\/,;()_-~+\" ", ' '); + _drop_special(res); + g_strstrip(res); + + return res; } diff --git a/core/tags/generic.cpp b/core/tags/generic.cpp index 534b1f0a..169462c4 100644 --- a/core/tags/generic.cpp +++ b/core/tags/generic.cpp @@ -7,8 +7,11 @@ GenericTag :: GenericTag() {} GenericTag :: GenericTag(const std::string &name) - : _name(name), _lower(string :: lowercase(name)) + : _name(name) { + gchar *g_lc = string_lowercase(name.c_str()); + _lower = g_lc; + g_free(g_lc); } GenericTag :: GenericTag(const GenericTag &tag) @@ -23,8 +26,12 @@ const std::string GenericTag :: primary_key() const void GenericTag :: read(File &file) { + gchar *g_lc; + _name = file.getline(); - _lower = string :: lowercase(_name); + g_lc = string_lowercase(_name.c_str()); + _lower = g_lc; + g_free(g_lc); } void GenericTag :: write(File &file) diff --git a/include/core/string.h b/include/core/string.h index 2310ef6b..c889d745 100644 --- a/include/core/string.h +++ b/include/core/string.h @@ -1,38 +1,22 @@ -/** +/* * Copyright 2015 (c) Anna Schumaker. + * + * NOTE: All of these functions allocate and return a new string. It is the + * caller's responsibility to free these strings by calling g_free(). */ #ifndef OCARINA_CORE_STRING_H #define OCARINA_CORE_STRING_H #include -#include - -/** - * Namespace for string formatting functions. - */ -namespace string -{ - - /** - * Convert a string to lowercase, stripping out special characters - * along the way. - * @param str Input string. - * @return The same string converted to lowercase. - */ - const std::string lowercase(const std::string &); -} -/* - * Convert number of seconds into a string with format mm:ss. - * This function allocates a new string that MUST be freed with g_free(). - */ +/* Convert number of seconds into a string with format mm:ss. */ gchar *string_sec2str(unsigned int); -/* - * Convert number of seconds into a long-form time string. - * This function allocates a new string that MUST be freed with g_free(). - */ +/* Convert number of seconds into a long-form time string. */ gchar *string_sec2str_long(unsigned int); +/* Convert the input string to lowercase, dropping special characters. */ +gchar *string_lowercase(const gchar *); + #endif /* OCARINA_CORE_STRING_H */ diff --git a/tests/core/string.cpp b/tests/core/string.cpp index f810f271..4e715002 100644 --- a/tests/core/string.cpp +++ b/tests/core/string.cpp @@ -56,19 +56,19 @@ void test_sec2str_long() void test_lowercase() { - test_equal(string :: lowercase(" "), ""); - test_equal(string :: lowercase(" test \ - text"), "test text"); - test_equal(string :: lowercase("test/text"), "test text"); - test_equal(string :: lowercase("Test, Text"), "test text"); - test_equal(string :: lowercase("Test? Text!"), "test text"); - test_equal(string :: lowercase("Test?123 Text!456"), "test123 text456"); - test_equal(string :: lowercase("Test?123 Text!456"), "test123 text456"); - test_equal(string :: lowercase("Test(text);123-456"), "test text 123 456"); - test_equal(string :: lowercase("Test((text));;123--456"), "test text 123 456"); - test_equal(string :: lowercase("! __test(TEXT) + __test(\"TEXT\")"), + test_equal(swap(string_lowercase(" ")), ""); + test_equal(swap(string_lowercase(" test \ + text")), "test text"); + test_equal(swap(string_lowercase("test/text")), "test text"); + test_equal(swap(string_lowercase("Test, Text")), "test text"); + test_equal(swap(string_lowercase("Test? Text!")), "test text"); + test_equal(swap(string_lowercase("Test?123 Text!456")), "test123 text456"); + test_equal(swap(string_lowercase("Test?123 Text!456")), "test123 text456"); + test_equal(swap(string_lowercase("Test(text);123-456")), "test text 123 456"); + test_equal(swap(string_lowercase("Test((text));;123--456")), "test text 123 456"); + test_equal(swap(string_lowercase("! __test(TEXT) + __test(\"TEXT\")")), "test text test text"); - test_equal(string :: lowercase("Test~tEXt\\123"), "test text 123"); + test_equal(swap(string_lowercase("Test~tEXt\\123")), "test text 123"); }