From 8d11577ff2b7caf846a31cbb57b2cf9eb3515961 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Thu, 25 Jun 2015 22:43:55 +0200
Subject: [PATCH] lib/icu/{Converter,Collate}: return AllocatedString

---
 src/SongFilter.cxx        | 15 ++++++++-------
 src/SongFilter.hxx        |  4 ++--
 src/fs/Charset.cxx        | 12 ++++++++++--
 src/lib/icu/Collate.cxx   | 22 +++++++++-------------
 src/lib/icu/Collate.hxx   |  3 ++-
 src/lib/icu/Converter.cxx | 28 +++++++++++-----------------
 src/lib/icu/Converter.hxx | 14 ++++++++------
 src/lib/icu/Util.cxx      |  8 +++++---
 src/lib/icu/Util.hxx      |  3 ++-
 test/TestIcu.cxx          | 10 +++++++---
 10 files changed, 64 insertions(+), 55 deletions(-)

diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx
index 5860b5ddd..4fc7145da 100644
--- a/src/SongFilter.cxx
+++ b/src/SongFilter.cxx
@@ -23,12 +23,12 @@
 #include "DetachedSong.hxx"
 #include "tag/Tag.hxx"
 #include "util/ConstBuffer.hxx"
+#include "util/StringAPI.hxx"
 #include "util/ASCII.hxx"
 #include "util/UriUtil.hxx"
 #include "lib/icu/Collate.hxx"
 
 #include <assert.h>
-#include <string.h>
 #include <stdlib.h>
 
 #define LOCATE_TAG_FILE_KEY     "file"
@@ -55,12 +55,12 @@ locate_parse_type(const char *str)
 }
 
 gcc_pure
-static std::string
+static AllocatedString<>
 ImportString(const char *p, bool fold_case)
 {
 	return fold_case
 		? IcuCaseFold(p)
-		: std::string(p);
+		: AllocatedString<>::Duplicate(p);
 }
 
 SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
@@ -70,7 +70,7 @@ SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
 }
 
 SongFilter::Item::Item(unsigned _tag, time_t _time)
-	:tag(_tag), time(_time)
+	:tag(_tag), value(nullptr), time(_time)
 {
 }
 
@@ -85,10 +85,11 @@ SongFilter::Item::StringMatch(const char *s) const
 	assert(tag != LOCATE_TAG_MODIFIED_SINCE);
 
 	if (fold_case) {
-		const std::string folded = IcuCaseFold(s);
-		return folded.find(value) != folded.npos;
+		const auto folded = IcuCaseFold(s);
+		assert(!folded.IsNull());
+		return StringFind(folded.c_str(), value.c_str()) != nullptr;
 	} else {
-		return s == value;
+		return StringIsEqual(s, value.c_str());
 	}
 }
 
diff --git a/src/SongFilter.hxx b/src/SongFilter.hxx
index ba5433df8..e15f0338f 100644
--- a/src/SongFilter.hxx
+++ b/src/SongFilter.hxx
@@ -20,10 +20,10 @@
 #ifndef MPD_SONG_FILTER_HXX
 #define MPD_SONG_FILTER_HXX
 
+#include "util/AllocatedString.hxx"
 #include "Compiler.h"
 
 #include <list>
-#include <string>
 
 #include <stdint.h>
 #include <time.h>
@@ -51,7 +51,7 @@ public:
 
 		bool fold_case;
 
-		std::string value;
+		AllocatedString<> value;
 
 		/**
 		 * For #LOCATE_TAG_MODIFIED_SINCE
diff --git a/src/fs/Charset.cxx b/src/fs/Charset.cxx
index b478959d0..f0fc1063c 100644
--- a/src/fs/Charset.cxx
+++ b/src/fs/Charset.cxx
@@ -116,7 +116,11 @@ PathToUTF8(PathTraitsFS::const_pointer path_fs)
 		return FixSeparators(path_fs);
 #ifdef HAVE_FS_CHARSET
 
-	return FixSeparators(fs_converter->ToUTF8(path_fs));
+	const auto buffer = fs_converter->ToUTF8(path_fs);
+	if (buffer.IsNull())
+		return PathTraitsUTF8::string();
+
+	return FixSeparators(PathTraitsUTF8::string(buffer.c_str()));
 #endif
 #endif
 }
@@ -141,7 +145,11 @@ PathFromUTF8(PathTraitsUTF8::const_pointer path_utf8)
 	if (fs_converter == nullptr)
 		return path_utf8;
 
-	return fs_converter->FromUTF8(path_utf8);
+	const auto buffer = fs_converter->FromUTF8(path_utf8);
+	if (buffer.IsNull())
+		return PathTraitsFS::string();
+
+	return PathTraitsFS::string(buffer.c_str());
 #endif
 }
 
diff --git a/src/lib/icu/Collate.cxx b/src/lib/icu/Collate.cxx
index 207252935..0df305130 100644
--- a/src/lib/icu/Collate.cxx
+++ b/src/lib/icu/Collate.cxx
@@ -19,6 +19,7 @@
 
 #include "config.h"
 #include "Collate.hxx"
+#include "util/AllocatedString.hxx"
 
 #ifdef HAVE_ICU
 #include "Util.hxx"
@@ -140,7 +141,7 @@ IcuCollate(const char *a, const char *b)
 #endif
 }
 
-std::string
+AllocatedString<>
 IcuCaseFold(const char *src)
 {
 #ifdef HAVE_ICU
@@ -152,7 +153,7 @@ 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];
@@ -165,20 +166,17 @@ IcuCaseFold(const char *src)
 	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;
+	return result;
 #elif defined(HAVE_GLIB)
 	char *tmp = g_utf8_casefold(src, -1);
-	std::string result(tmp);
+	auto result = AllocatedString<>::Duplicate(tmp);
 	g_free(tmp);
+	return result;
 #else
 	size_t size = strlen(src) + 1;
 	auto buffer = new char[size];
@@ -194,9 +192,7 @@ IcuCaseFold(const char *src)
 	assert(nbytes < size);
 	assert(buffer[nbytes] == 0);
 
-	std::string result(buffer, nbytes);
-	delete[] buffer;
+	return AllocatedString<>::Donate(buffer);
 #endif
-	return result;
 }
 
diff --git a/src/lib/icu/Collate.hxx b/src/lib/icu/Collate.hxx
index 9f8ea43ab..0ad3b24ff 100644
--- a/src/lib/icu/Collate.hxx
+++ b/src/lib/icu/Collate.hxx
@@ -26,6 +26,7 @@
 #include <string>
 
 class Error;
+template<typename T> class AllocatedString;
 
 bool
 IcuCollateInit(Error &error);
@@ -38,7 +39,7 @@ int
 IcuCollate(const char *a, const char *b);
 
 gcc_pure gcc_nonnull_all
-std::string
+AllocatedString<char>
 IcuCaseFold(const char *src);
 
 #endif
diff --git a/src/lib/icu/Converter.cxx b/src/lib/icu/Converter.cxx
index ea10a9a03..fad1f4d66 100644
--- a/src/lib/icu/Converter.cxx
+++ b/src/lib/icu/Converter.cxx
@@ -22,6 +22,7 @@
 #include "Error.hxx"
 #include "util/Error.hxx"
 #include "util/Macros.hxx"
+#include "util/AllocatedString.hxx"
 #include "util/WritableBuffer.hxx"
 #include "util/ConstBuffer.hxx"
 
@@ -80,7 +81,7 @@ IcuConverter::Create(const char *charset, Error &error)
 #ifdef HAVE_ICU
 #elif defined(HAVE_GLIB)
 
-static std::string
+static AllocatedString<char>
 DoConvert(GIConv conv, const char *src)
 {
 	// TODO: dynamic buffer?
@@ -93,14 +94,14 @@ DoConvert(GIConv conv, const char *src)
 	size_t n = g_iconv(conv, &in, &in_left, &out, &out_left);
 
 	if (n == static_cast<size_t>(-1) || in_left > 0)
-		return std::string();
+		return nullptr;
 
-	return std::string(buffer, sizeof(buffer) - out_left);
+	return AllocatedString::Duplicate(buffer, sizeof(buffer) - out_left);
 }
 
 #endif
 
-std::string
+AllocatedString<char>
 IcuConverter::ToUTF8(const char *s) const
 {
 #ifdef HAVE_ICU
@@ -118,23 +119,16 @@ IcuConverter::ToUTF8(const char *s) const
 		       &source, source + strlen(source),
 		       nullptr, true, &code);
 	if (code != U_ZERO_ERROR)
-		return std::string();
+		return nullptr;
 
 	const size_t target_length = target - buffer;
-	const auto u = UCharToUTF8({buffer, target_length});
-	if (u.IsNull())
-		return std::string();
-
-	std::string result(u.data, u.size);
-	delete[] u.data;
-	return result;
-
+	return UCharToUTF8({buffer, target_length});
 #elif defined(HAVE_GLIB)
 	return DoConvert(to_utf8, s);
 #endif
 }
 
-std::string
+AllocatedString<char>
 IcuConverter::FromUTF8(const char *s) const
 {
 #ifdef HAVE_ICU
@@ -142,7 +136,7 @@ IcuConverter::FromUTF8(const char *s) const
 
 	const auto u = UCharFromUTF8(s);
 	if (u.IsNull())
-		return std::string();
+		return nullptr;
 
 	ucnv_resetFromUnicode(converter);
 
@@ -157,9 +151,9 @@ IcuConverter::FromUTF8(const char *s) const
 	delete[] u.data;
 
 	if (code != U_ZERO_ERROR)
-		return std::string();
+		return nullptr;
 
-	return std::string(buffer, target);
+	return AllocatedString<>::Duplicate(buffer, target);
 
 #elif defined(HAVE_GLIB)
 	return DoConvert(from_utf8, s);
diff --git a/src/lib/icu/Converter.hxx b/src/lib/icu/Converter.hxx
index 3eba86c25..fd5ea2132 100644
--- a/src/lib/icu/Converter.hxx
+++ b/src/lib/icu/Converter.hxx
@@ -33,14 +33,14 @@
 
 #ifdef HAVE_ICU_CONVERTER
 
-#include <string>
-
 class Error;
 
 #ifdef HAVE_ICU
 struct UConverter;
 #endif
 
+template<typename T> class AllocatedString;
+
 /**
  * This class can convert strings with a certain character set to and
  * from UTF-8.
@@ -77,17 +77,19 @@ public:
 
 	/**
 	 * Convert the string to UTF-8.
-	 * Returns empty string on error.
+	 *
+	 * Returns AllocatedString::Null() on error.
 	 */
 	gcc_pure gcc_nonnull_all
-	std::string ToUTF8(const char *s) const;
+	AllocatedString<char> ToUTF8(const char *s) const;
 
 	/**
 	 * Convert the string from UTF-8.
-	 * Returns empty string on error.
+	 *
+	 * Returns AllocatedString::Null() on error.
 	 */
 	gcc_pure gcc_nonnull_all
-	std::string FromUTF8(const char *s) const;
+	AllocatedString<char> FromUTF8(const char *s) const;
 };
 
 #endif
diff --git a/src/lib/icu/Util.cxx b/src/lib/icu/Util.cxx
index ae47423ad..92f1de5aa 100644
--- a/src/lib/icu/Util.cxx
+++ b/src/lib/icu/Util.cxx
@@ -19,6 +19,7 @@
 
 #include "config.h"
 #include "Util.hxx"
+#include "util/AllocatedString.hxx"
 #include "util/WritableBuffer.hxx"
 #include "util/ConstBuffer.hxx"
 
@@ -49,7 +50,7 @@ UCharFromUTF8(const char *src)
 	return { dest, size_t(dest_length) };
 }
 
-WritableBuffer<char>
+AllocatedString<>
 UCharToUTF8(ConstBuffer<UChar> src)
 {
 	assert(!src.IsNull());
@@ -57,7 +58,7 @@ UCharToUTF8(ConstBuffer<UChar> src)
 	/* worst-case estimate */
 	size_t dest_capacity = 4 * src.size;
 
-	char *dest = new char[dest_capacity];
+	char *dest = new char[dest_capacity + 1];
 
 	UErrorCode error_code = U_ZERO_ERROR;
 	int32_t dest_length;
@@ -68,5 +69,6 @@ UCharToUTF8(ConstBuffer<UChar> src)
 		return nullptr;
 	}
 
-	return { dest, size_t(dest_length) };
+	dest[dest_length] = 0;
+	return AllocatedString<>::Donate(dest);
 }
diff --git a/src/lib/icu/Util.hxx b/src/lib/icu/Util.hxx
index f26b72494..f2d99d0e6 100644
--- a/src/lib/icu/Util.hxx
+++ b/src/lib/icu/Util.hxx
@@ -26,6 +26,7 @@
 
 template<typename T> struct WritableBuffer;
 template<typename T> struct ConstBuffer;
+template<typename T> class AllocatedString;
 
 /**
  * Wrapper for u_strFromUTF8().  The returned pointer must be freed
@@ -38,7 +39,7 @@ UCharFromUTF8(const char *src);
  * Wrapper for u_strToUTF8().  The returned pointer must be freed with
  * delete[].
  */
-WritableBuffer<char>
+AllocatedString<char>
 UCharToUTF8(ConstBuffer<UChar> src);
 
 #endif
diff --git a/test/TestIcu.cxx b/test/TestIcu.cxx
index 9d525d698..484af4f22 100644
--- a/test/TestIcu.cxx
+++ b/test/TestIcu.cxx
@@ -4,6 +4,8 @@
 
 #include "config.h"
 #include "lib/icu/Converter.hxx"
+#include "util/AllocatedString.hxx"
+#include "util/StringAPI.hxx"
 #include "util/Error.hxx"
 
 #include <cppunit/TestFixture.h>
@@ -49,15 +51,17 @@ public:
 
 		for (const auto i : invalid_utf8) {
 			auto f = converter->FromUTF8(i);
-			CPPUNIT_ASSERT_EQUAL(true, f.empty());
+			CPPUNIT_ASSERT_EQUAL(true, f.IsNull());
 		}
 
 		for (const auto i : latin1_tests) {
 			auto f = converter->FromUTF8(i.utf8);
-			CPPUNIT_ASSERT_EQUAL(true, f == i.other);
+			CPPUNIT_ASSERT_EQUAL(true, StringIsEqual(f.c_str(),
+								 i.other));
 
 			auto t = converter->ToUTF8(i.other);
-			CPPUNIT_ASSERT_EQUAL(true, t == i.utf8);
+			CPPUNIT_ASSERT_EQUAL(true, StringIsEqual(t.c_str(),
+								 i.utf8));
 		}
 
 		delete converter;