diff options
Diffstat (limited to 'src/lib/icu/Collate.cxx')
-rw-r--r-- | src/lib/icu/Collate.cxx | 151 |
1 files changed, 82 insertions, 69 deletions
diff --git a/src/lib/icu/Collate.cxx b/src/lib/icu/Collate.cxx index 17b536b37..dc8598a7a 100644 --- a/src/lib/icu/Collate.cxx +++ b/src/lib/icu/Collate.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,8 +19,10 @@ #include "config.h" #include "Collate.hxx" +#include "util/AllocatedString.hxx" #ifdef HAVE_ICU +#include "Util.hxx" #include "Error.hxx" #include "util/WritableBuffer.hxx" #include "util/ConstBuffer.hxx" @@ -29,13 +31,17 @@ #include <unicode/ucol.h> #include <unicode/ustring.h> -#elif defined(HAVE_GLIB) -#include <glib.h> #else #include <algorithm> #include <ctype.h> #endif +#ifdef WIN32 +#include "Win32.hxx" +#include "util/AllocatedString.hxx" +#include <windows.h> +#endif + #include <assert.h> #include <string.h> #include <strings.h> @@ -71,50 +77,6 @@ IcuCollateFinish() ucol_close(collator); } -static WritableBuffer<UChar> -UCharFromUTF8(const char *src) -{ - assert(src != nullptr); - - const size_t src_length = strlen(src); - const size_t dest_capacity = src_length; - UChar *dest = new UChar[dest_capacity]; - - UErrorCode error_code = U_ZERO_ERROR; - int32_t dest_length; - u_strFromUTF8(dest, dest_capacity, &dest_length, - src, src_length, - &error_code); - if (U_FAILURE(error_code)) { - delete[] dest; - return nullptr; - } - - return { dest, size_t(dest_length) }; -} - -static WritableBuffer<char> -UCharToUTF8(ConstBuffer<UChar> src) -{ - assert(!src.IsNull()); - - /* worst-case estimate */ - size_t dest_capacity = 4 * src.size; - - char *dest = new char[dest_capacity]; - - UErrorCode error_code = U_ZERO_ERROR; - int32_t dest_length; - u_strToUTF8(dest, dest_capacity, &dest_length, src.data, src.size, - &error_code); - if (U_FAILURE(error_code)) { - delete[] dest; - return nullptr; - } - - return { dest, size_t(dest_length) }; -} - #endif gcc_pure @@ -150,14 +112,32 @@ IcuCollate(const char *a, const char *b) return result; #endif -#elif defined(HAVE_GLIB) - return g_utf8_collate(a, b); +#elif defined(WIN32) + const auto wa = MultiByteToWideChar(CP_UTF8, a); + const auto wb = MultiByteToWideChar(CP_UTF8, b); + if (wa.IsNull()) + return wb.IsNull() ? 0 : -1; + else if (wb.IsNull()) + return 1; + + auto result = CompareStringEx(LOCALE_NAME_INVARIANT, + LINGUISTIC_IGNORECASE, + wa.c_str(), -1, + wb.c_str(), -1, + nullptr, nullptr, 0); + if (result != 0) + /* "To maintain the C runtime convention of comparing + strings, the value 2 can be subtracted from a + nonzero return value." */ + result -= 2; + + return result; #else - return strcasecmp(a, b); + return strcoll(a, b); #endif } -std::string +AllocatedString<> IcuCaseFold(const char *src) { #ifdef HAVE_ICU @@ -169,37 +149,70 @@ IcuCaseFold(const char *src) const auto u = UCharFromUTF8(src); if (u.IsNull()) - return std::string(src); + return AllocatedString<>::Duplicate(src); size_t folded_capacity = u.size * 2u; UChar *folded = new UChar[folded_capacity]; UErrorCode error_code = U_ZERO_ERROR; size_t folded_length = u_strFoldCase(folded, folded_capacity, - u.data, u.size, - U_FOLD_CASE_DEFAULT, - &error_code); + u.data, u.size, + U_FOLD_CASE_DEFAULT, + &error_code); delete[] u.data; if (folded_length == 0 || error_code != U_ZERO_ERROR) { delete[] folded; - return std::string(src); + return AllocatedString<>::Duplicate(src); } - auto result2 = UCharToUTF8({folded, folded_length}); + auto result = UCharToUTF8({folded, folded_length}); delete[] folded; - if (result2.IsNull()) - return std::string(src); - - std::string result(result2.data, result2.size); - delete[] result2.data; -#elif defined(HAVE_GLIB) - char *tmp = g_utf8_casefold(src, -1); - std::string result(tmp); - g_free(tmp); + return result; + +#elif defined(WIN32) + const auto u = MultiByteToWideChar(CP_UTF8, src); + if (u.IsNull()) + return AllocatedString<>::Duplicate(src); + + const int size = LCMapStringEx(LOCALE_NAME_INVARIANT, + LCMAP_SORTKEY|LINGUISTIC_IGNORECASE, + u.c_str(), -1, nullptr, 0, + nullptr, nullptr, 0); + if (size <= 0) + return AllocatedString<>::Duplicate(src); + + auto buffer = new wchar_t[size]; + if (LCMapStringEx(LOCALE_NAME_INVARIANT, + LCMAP_SORTKEY|LINGUISTIC_IGNORECASE, + u.c_str(), -1, buffer, size, + nullptr, nullptr, 0) <= 0) { + delete[] buffer; + return AllocatedString<>::Duplicate(src); + } + + auto result = WideCharToMultiByte(CP_UTF8, buffer); + delete[] buffer; + if (result.IsNull()) + return AllocatedString<>::Duplicate(src); + + return result; + #else - std::string result(src); - std::transform(result.begin(), result.end(), result.begin(), tolower); + size_t size = strlen(src) + 1; + auto buffer = new char[size]; + size_t nbytes = strxfrm(buffer, src, size); + if (nbytes >= size) { + /* buffer too small - reallocate and try again */ + delete[] buffer; + size = nbytes + 1; + buffer = new char[size]; + nbytes = strxfrm(buffer, src, size); + } + + assert(nbytes < size); + assert(buffer[nbytes] == 0); + + return AllocatedString<>::Donate(buffer); #endif - return result; } |