1 /*
2 	Copyright (C) 2005-2007 Feeling Software Inc.
3 	Portions of the code are:
4 	Copyright (C) 2005-2007 Sony Computer Entertainment America
5 
6 	MIT License: http://www.opensource.org/licenses/mit-license.php
7 */
8 
9 /**
10 	@file FUString.h
11 	This file includes FUStringBuilder.h and FUStringConversion.h
12 	and defines important string-related macros and inline functions.
13 */
14 
15 #ifndef _FU_STRING_H_
16 #define _FU_STRING_H_
17 
18 #ifndef _FM_ARRAY_H_
19 #include "FMath/FMArray.h"
20 #endif // _FM_ARRAY_H_
21 
22 /** An empty UTF-8 string. This string is returned in many functions when there is an error. */
23 extern FCOLLADA_EXPORT const char* emptyCharString;
24 /** An empty Unicode string. This string is returned in many functions when there is an error. */
25 extern FCOLLADA_EXPORT const fchar* emptyFCharString;
26 
27 // Already documented elsewhere.
28 namespace fm
29 {
30 	/** A string template.
31 		Intentionally has an interface similar to STL.
32 		You should use fm::string for UTF8 strings and
33 		fstring for UNICODE strings.
34 		@ingroup FUtils */
35 	template <class CH>
36 	class FCOLLADA_EXPORT stringT : public fm::vector<CH, true>
37 	{
38 	public:
39 		typedef fm::vector<CH, true> Parent; /**< The parent class. */
40 
41 	public:
42 		/** Constant for infinity length.
43 			This important constant is used throughout the
44 			class to indicate infinity or values not found. */
45 		static const size_t npos = ~(size_t)0;
46 
47 		/** Default constructor. Defaults to an empty string. */
stringT()48 		stringT() : Parent() {}
49 
50 		/** Copy constructor.
51 			@param c The string to clone. */
stringT(const stringT & c)52 		stringT(const stringT& c) : Parent(c) {}
53 
54 		/** Copy constructor.
55 			@param c A NULL-terminated character buffer to clone. */
stringT(const CH * c)56 		stringT(const CH* c) : Parent()
57 		{
58 			append(c);
59 			if (c == NULL || (*c) == 0) Parent::push_back((CH) 0);
60 		}
61 
62 		/** Copy constructor.
63 			@param c A character buffer to clone.
64 			@param length A partial length to copy.
65 				Use stringT::npos for copying full NULL-terminated strings. */
stringT(const CH * c,size_t length)66 		stringT(const CH* c, size_t length) : Parent()
67 		{
68 			if (c == NULL || length == 0) return;
69 
70 			if (length != npos)
71 			{
72 				Parent::resize(length + 1);
73 				memcpy(Parent::begin(), c, sizeof(CH) * length);
74 				Parent::back() = 0; // NULL-terminate.
75 			}
76 			else
77 			{
78 				append(c);
79 				if (c == NULL || (*c) == 0) Parent::push_back((CH) 0);
80 			}
81 		}
82 
83 		/** Constructor.
84 			@param length The number of characters to repeat in the string.
85 			@param c The character value to repeat within the string. */
stringT(size_t length,const CH & c)86 		stringT(size_t length, const CH& c) : Parent()
87 		{
88 			Parent::reserve(length + 1);
89 			Parent::insert(Parent::begin(), length, c);
90 			Parent::push_back((CH) 0);
91 		}
92 
93 		/** Retrieves the length of the string.
94 			This function is NULL-termination aware.
95 			@return The length of the string. */
length()96 		inline size_t length() const { return Parent::size() > 1 ? Parent::size() - 1 : 0; }
size()97 		inline size_t size() const { return Parent::size() > 1 ? Parent::size() - 1 : 0; } /**< See above. */
98 
99 		/** Retrieves the last element of the string.
100 			This function is NULL-termination aware.
101 			@return The last element of the string. */
back()102 		inline CH& back() { return *(Parent::end() - 2); }
back()103 		inline const CH& back() const { return *(Parent::end() - 2); } /**< See above. */
104 
105 		/** Removes the last character from a string.
106 			This function is NULL-termination aware. */
pop_back()107 		inline void pop_back() { if (Parent::size() > 0) { Parent::pop_back(); Parent::back() = 0; } }
108 
109 		/** Retrieves whether the string contains useful data.
110 			This function differs from the parent because it checks for NULL-termination.
111 			@return Whether the string contains useful data. */
empty()112 		inline bool empty() const { return Parent::size() <= 1; }
113 
114 		/** Retrieves a segment of the string.
115 			@param start The index of the first character to extract.
116 			@param count The number of characters to extract. When the count
117 				is 'npos', all the remaining characters are extracted.
118 			@return The partial string. */
119 		stringT substr(size_t start, size_t count = npos) const
120 		{
121 			if (start >= length()) return stringT();
122 			if (count == npos || count + start > length()) count = length() - start;
123 			return stringT(c_str() + start, count);
124 		}
125 
126 		/** Appends a string to this string.
127 			@param str A second string. */
append(const stringT & str)128 		inline void append(const stringT& str)
129 		{
130 			insert(npos, str);
131 		}
132 
133 		/** Appends a NULL-terminated character buffer to this string.
134 			@param str A NULL-terminated character buffer.
135 			@param count The number of characters to append. If the count
136 				is 'npos', all available characters are appended. */
137 		inline void append(const CH* str, size_t count=npos)
138 		{
139 			insert(npos, str, count);
140 		}
141 
142 		/** Appends one character to this string.
143 			@param c A character. */
append(const CH & c)144 		void append(const CH& c)
145 		{
146 			if (c != 0)
147 			{
148 				size_t originalSize = length();
149 				Parent::resize(originalSize + 2);
150 				*(Parent::end() - 2) = c;
151 				Parent::back() = 0; // NULL-terminate
152 			}
153 		}
154 
155 		/** Inserts a character buffer in this string.
156 			@param offset The position at which to insert the character buffer.
157 			@param str A NULL-terminated character buffer.
158 			@param count The number of characters to append. If the count
159 				is 'npos', all available characters are appended. */
160 		void insert(size_t offset, const CH* str, size_t count=npos)
161 		{
162 			if (str != NULL && (*str != 0))
163 			{
164 				size_t originalSize = length();
165 				offset = min(offset, originalSize);
166 
167 				size_t str_length = 0;
168 				const CH* s = str;
169 				while(*s != 0 && str_length < count) { ++s; ++str_length; }
170 				resize(originalSize + str_length);
171 				if (offset < originalSize)
172 				{
173 					memmove(Parent::begin() + offset + str_length, Parent::begin() + offset, (originalSize - offset) * sizeof(CH));
174 				}
175 				memcpy(Parent::begin() + offset, str, sizeof(CH) * str_length);
176 				Parent::back() = 0; // NULL-terminate
177 			}
178 		}
179 
180 		/** Inserts a string in this string.
181 			@param offset The position at which to insert the string.
182 			@param str A second string. */
insert(size_t offset,const stringT & str)183 		void insert(size_t offset, const stringT& str)
184 		{
185 			size_t str_length = str.length();
186 			if (str_length > 0)
187 			{
188 				size_t originalSize = length();
189 				offset = min(offset, originalSize);
190 				resize(originalSize + str_length);
191 				if (offset < originalSize)
192 				{
193 					memmove(Parent::begin() + offset + str_length, Parent::begin() + offset, (originalSize - offset) * sizeof(CH));
194 				}
195 				memcpy(Parent::begin() + offset, str.c_str(), sizeof(CH) * str_length);
196 			}
197 		}
198 
199 		/** Retrieves the character buffer attached to this string.
200 			@return The NULL-terminated character buffer for this string. */
c_str()201 		const CH* c_str() const
202 		{
203 			static CH empty = 0;
204 			if (Parent::size() == 0) return &empty;
205 			return Parent::begin();
206 		}
207 		inline operator const CH*() const { return c_str(); } /**< See above. */
208 
209 		/** Retrieves the position of the first matching character found within the string.
210 			@param character The character to match.
211 			@param offset An offset at which to start searching. Defaults to zero.
212 			@return The position of the first matching character. If 'npos' is returned, the
213 				character was not matched within the given (sub)string. */
214 		size_t find(const CH& character, size_t offset=0) const
215 		{
216 			if (character > 0)
217 			{
218 				for (const CH* it = Parent::begin() + offset; it < Parent::end(); ++it)
219 				{
220 					if ((*it) == character) return it - Parent::begin();
221 				}
222 			}
223 			return npos;
224 		}
225 
226 		/** Retrieves the position of the first matching string found within the string.
227 			@param str The string to match. If the string is zero-terminated, the null
228 				character will not be included in the search.
229 			@param offset An offset at which to start searching. Defaults to zero.
230 			@return The position of the first matching string. If 'npos' is returned, the
231 				string was not matched within the given (sub)string. */
232 		size_t find(const stringT& str, size_t offset=0) const
233 		{
234 			if (str.length() > 0 && length() >= str.length())
235 			{
236 				CH firstMatch = str.front();
237 				const CH* end_fence = Parent::end() - str.size() + 1;
238 				for (const CH* it = Parent::begin() + offset; it < end_fence; ++it)
239 				{
240 					if ((*it) == firstMatch)
241 					{
242 						const CH* it2 = it;
243 						const CH* sit = str.begin();
244 						const CH* endIt = (*(str.end() - 1) == 0) ? str.end() - 1 : str.end();
245 						for (; it2 != Parent::end() && sit != endIt; ++sit, ++it2)
246 						{
247 							if ((*sit) != (*it2)) break;
248 						}
249 						if (sit == endIt) return it - Parent::begin();
250 					}
251 				}
252 			}
253 			return npos;
254 		}
255 
256 		/** Retrieves the position of the first matching character buffer found within the string.
257 			@param c The character buffer to match.
258 			@param offset An offset at which to start searching. Defaults to zero.
259 			@return The position of the first matching character buffer. If 'npos' is returned, the
260 				character buffer was not matched within the given (sub)string. */
261 		size_t find(const CH* c, size_t offset=0) const
262 		{
263 			size_t length = 0; const CH* d = c; while (*d != 0) { ++length; ++d; }
264 			if (length > 0 && Parent::size() >= length)
265 			{
266 				const CH* end_fence = Parent::end() - length + 1;
267 				for (const CH* it = Parent::begin() + offset; it < end_fence; ++it)
268 				{
269 					if ((*it) == (*c))
270 					{
271 						const CH* it2 = it;
272 						for (d = c; it2 != Parent::end() && (*d) != 0; ++it2, ++d)
273 						{
274 							if ((*it2) != (*d)) break;
275 						}
276 						if (*d == 0) return it - Parent::begin();
277 					}
278 				}
279 			}
280 			return npos;
281 		}
282 
283 		/** Retrieves the position of the last matching character found within the string.
284 			@param character The character to match.
285 			@param offset An offset at which to start searching. Defaults to zero.
286 			@return The position of the last matching character. If 'npos' is returned, the
287 				character was not matched within the given (sub)string. */
288 		size_t rfind(const CH& character, size_t offset=0) const
289 		{
290 			size_t ret = npos;
291 			if (character > 0)
292 			{
293 				for (const CH* it = Parent::begin() + offset; it < Parent::end(); ++it)
294 				{
295 					if ((*it) == character) ret = it - Parent::begin();
296 				}
297 			}
298 			return ret;
299 		}
300 
301 		/** Retrieves the position of the last matching string found within the string.
302 			@param str The string to match. If the string is zero-terminated, the null
303 				character will not be included in the search.
304 			@param offset An offset at which to start searching. Defaults to zero.
305 			@return The position of the last matching string. If 'npos' is returned, the
306 				string was not matched within the given (sub)string. */
307 		size_t rfind(const stringT& str, size_t offset=0) const
308 		{
309 			size_t ret = npos;
310 			if (str.length() > 0 && length() >= str.length())
311 			{
312 				CH firstMatch = str.front();
313 				const CH* end_fence = Parent::end() - str.size() + 1;
314 				for (const CH* it = Parent::begin() + offset; it < end_fence; ++it)
315 				{
316 					if ((*it) == firstMatch)
317 					{
318 						const CH* it2 = it;
319 						const CH* sit = str.begin();
320 						const CH* endIt = (*(str.end() - 1) == 0) ? str.end() - 1 : str.end();
321 						for (; it2 != Parent::end() && sit != endIt; ++sit, ++it2)
322 						{
323 							if ((*sit) != (*it2)) break;
324 						}
325 						if (sit == endIt) ret = it - Parent::begin();
326 					}
327 				}
328 			}
329 			return ret;
330 		}
331 
332 		/** Retrieves the position of the last matching character buffer found within the string.
333 			@param c The character buffer to match.
334 			@param offset An offset at which to start searching. Defaults to zero.
335 			@return The position of the last matching character buffer. If 'npos' is returned, the
336 				character buffer was not matched within the given (sub)string. */
337 		size_t rfind(const CH* c, size_t offset=0) const
338 		{
339 			size_t ret = npos;
340 			size_t length = 0; const CH* d = c; while (*d != 0) { ++length; ++d; }
341 			if (length > 0 && Parent::size() >= length)
342 			{
343 				const CH* end_fence = Parent::end() - length + 1;
344 				for (const CH* it = Parent::begin() + offset; it < end_fence; ++it)
345 				{
346 					if ((*it) == (*c))
347 					{
348 						const CH* it2 = it;
349 						for (d = c; it2 != Parent::end() && (*d) != 0; ++it2, ++d)
350 						{
351 							if ((*it2) != (*d)) break;
352 						}
353 						if (*d == 0) ret = it - Parent::begin();
354 					}
355 				}
356 			}
357 			return ret;
358 		}
359 
360 		/** Retrieves the position of the first matching character from a list of possible characters.
361 			@param c A list of possible characters to match.
362 			@param offset An offset at which to start searching. Defaults to zero.
363 			@return The position of the first matching character within the list of possible characters.
364 				If 'npos' is returned, none of the possible characters were found within the (sub)string. */
365 		size_t find_first_of(const CH* c, size_t offset=0) const
366 		{
367 			size_t length = 0; const CH* d = c; while (*d != 0) { ++length; ++d; }
368 			if (length > 0 && Parent::size() >= length)
369 			{
370 				for (const CH* it = Parent::begin() + offset; it < Parent::end(); ++it)
371 				{
372 					d = c;
373 					while (*d != 0 && *d != *it) { ++d; }
374 					if (*d != 0) return it - Parent::begin();
375 				}
376 			}
377 			return npos;
378 		}
379 
380 		/** Retrieves the position of the last matching character from a list of possible characters.
381 			@param c A list of possible characters to match.
382 			@param offset An offset (from the end of the string) at which to start searching. Defaults to zero.
383 			@return The position of the last matching character within the list of possible characters.
384 				If 'npos' is returned, none of the possible characters were found within the (sub)string. */
385 		size_t find_last_of(const CH* c, size_t offset=0) const
386 		{
387 			size_t ret = length() - offset; // UNOPTIMIZE. Didn't feel like this is an important-enough function.
388 			size_t length = 0; const CH* d = c; while (*d != 0) { ++length; ++d; }
389 			if (length > 0 && Parent::size() >= length)
390 			{
391 				const CH* end = Parent::end() - (length + offset);
392 				const CH* begin = Parent::begin();
393 				for (const CH* it = end; it >= begin; --it)
394 				{
395 					d = c;
396 					while (*d != 0)
397 					{
398 						if (*d == *it) return ret;
399 						++d;
400 					}
401 					--ret;
402 				}
403 			}
404 			return npos;
405 		}
406 
407 		/** Removes a range of characters from a string.
408 			@param start The start of the range of characters to remove.
409 				Defaults to zero.
410 			@param end The end of the range of characters to remove.
411 				The character at this position will not be removed so
412 				that if (start==end), no operation is done.
413 				Defaults to npos, which indicates that all characters from
414 				the start of the range should be removed. */
415 		void erase(size_t start=0, size_t end=npos)
416 		{
417 			if (start < length() && start < end) // covers size() == 0.
418 			{
419 				if (end > length()) end = length();
420 				Parent::erase(Parent::begin() + start, Parent::begin() + end);
421 			}
422 		}
423 
424 		/** Override the fm::vector resize.  That call
425 			does not handle the assumption this class makes that
426 			every string is null terminated.
427 
428 			The fm::string resize command will automatically create
429 			a null terminated string of length size
430 			@param size Length of resulting string */
resize(size_t size)431 		void resize(size_t size)
432 		{
433 			Parent::resize(size + 1);
434 			Parent::back() = 0; // NULL-terminate
435 		}
436 
437 		/** Override the fm::vector resize.  That call
438 			does not handle the assumption this class makes that
439 			every string is null terminated.
440 
441 			Sets the number of values contained in the list.
442 			@param count The new number of values contained in the list.
443 			@param value The value to assign to the new entries in the list. */
resize(size_t count,const CH & value)444 		void resize(size_t count, const CH& value)
445 		{
446 			Parent::resize(count + 1, value);
447 			Parent::back() = 0; // NULL-terminate
448 		}
449 	};
450 
451 	/** A string of UTF8 characters. */
452 	typedef stringT<char> string;
453 
454 	/** Concatenates two strings.
455 		@param A A first string.
456 		@param B A second string.
457 		@return The concatenation of the two strings. */
458 	template <class CharT> stringT<CharT> operator+(const stringT<CharT>& A, const stringT<CharT>& B) { stringT<CharT> C = A; C.append(B); return C; }
459 	template <class CharT> stringT<CharT> operator+(const CharT* A, const stringT<CharT>& B) { stringT<CharT> C = A; C.append(B); return C; } /**< See above. */
460 	template <class CharT> stringT<CharT> operator+(const stringT<CharT>& A, const CharT* B) { stringT<CharT> C = A; C.append(B); return C; } /**< See above. */
461 	template <class CharT> stringT<CharT>& operator+=(stringT<CharT>& A, const stringT<CharT>& B) { A.append(B); return A; } /**< See above. */
462 	template <class CharT> stringT<CharT>& operator+=(stringT<CharT>& A, const CharT* B) { A.append(B); return A; } /**< See above. */
463 
464 	/** Appends a character to a string.
465 		@param A A string.
466 		@param B A character.
467 		@return The concatenation of the string with the character. */
468 	template <class CharT> stringT<CharT>& operator+=(stringT<CharT>& A, const CharT& B) { A.append(B); return A; }
469 
470 	/** Retrieves whether a first string is lesser than a second string.
471 		This comparison is done solely on the character buffers and not the lengths.
472 		@param A A first string.
473 		@param B A second string.
474 		@return Whether the first string is lesser than the second string. */
475 	template <class CharT> bool operator<(const stringT<CharT>& A, const stringT<CharT>& B)
476 	{
477 		const CharT* a = A.c_str(); const CharT* b = B.c_str();
478 		while ((*a) != 0 && (*b) != 0 && (*a) == (*b)) { ++a; ++b; }
479 		return (*a) < (*b);
480 	}
481 
482 	/** Retrieves whether a first string is equal to a second string.
483 		@param A A first string.
484 		@param B A second string.
485 		@return Whether the first string is equal to the second string. */
486 	template <class CharT> bool operator==(const stringT<CharT>& A, const stringT<CharT>& B)
487 	{
488 		if (A.length() != B.length()) return false;
489 		const CharT* a = A.c_str(); const CharT* b = B.c_str();
490 		while ((*a) != 0 && (*a) == (*b)) { ++a; ++b; }
491 		return (*a) == (*b);
492 	}
493 
494 	/** Retrieves whether a first string differs from a second string.
495 		@param A A first string.
496 		@param B A second string.
497 		@return Whether the first string differs from the second string. */
498 	template <class CharT> bool operator!=(const stringT<CharT>& A, const stringT<CharT>& B)
499 	{
500 		if (A.length() != B.length()) return true;
501 		const CharT* a = A.c_str(); const CharT* b = B.c_str();
502 		while ((*a) != 0 && (*a) == (*b)) { ++a; ++b; }
503 		return (*a) != (*b);
504 	}
505 
506 	/** Retrieves whether a first string differs from a second string.
507 		@param A A first string.
508 		@param B A second string.
509 		@return Whether the first string differs from the second string. */
510 	template <class CharT> bool operator!=(const stringT<CharT>& A, const CharT* B)
511 	{
512 		if (B == NULL) return true;
513 		size_t B_length = 0; { const CharT* b = B; while (*b != 0) { ++b; ++B_length; } }
514 		if (A.length() != B_length) return true;
515 		const CharT* a = A.c_str(); const CharT* b = B;
516 		while ((*a) != 0 && (*a) == (*b)) { ++a; ++b; }
517 		return (*a) != (*b);
518 	}
519 };
520 
521 /** A string of UNICODE characters. */
522 typedef fm::stringT<fchar> fstring;
523 
524 /** A dynamically-sized array of Unicode strings. */
525 typedef fm::vector<fstring> FStringList;
526 
527 /** A dynamically-sized array of simple strings. */
528 typedef fm::vector<fm::string> StringList;
529 
530 /** Returns whether two 8-bit strings are equivalent. This is a case-sensitive comparison.
531 	@param sz1 The first 8-bit string to compare.
532 	@param sz2 The second 8-bit string to compare.
533 	@return Whether the two 8-bit strings are equivalent. */
IsEquivalent(const char * sz1,const char * sz2)534 inline bool IsEquivalent(const char* sz1, const char* sz2) { return strcmp(sz1, sz2) == 0; }
IsEquivalent(const fm::string & sz1,const char * sz2)535 inline bool IsEquivalent(const fm::string& sz1, const char* sz2) { return strcmp(sz1.c_str(), sz2) == 0; } /**< See above. */
IsEquivalent(const char * sz1,const fm::string & sz2)536 inline bool IsEquivalent(const char* sz1, const fm::string& sz2) { return strcmp(sz1, sz2.c_str()) == 0; } /**< See above. */
IsEquivalent(const fm::string & sz1,const fm::string & sz2)537 inline bool IsEquivalent(const fm::string& sz1, const fm::string& sz2) { return strcmp(sz1.c_str(), sz2.c_str()) == 0; } /**< See above. */
538 
539 /** Returns whether two 8-bit strings are equivalent. This is a case-insensitive comparison.
540 	@param sz1 The first 8-bit string to compare.
541 	@param sz2 The second 8-bit string to compare.
542 	@return Whether the two 8-bit strings are equivalent. */
IsEquivalentI(const char * sz1,const char * sz2)543 inline bool IsEquivalentI(const char* sz1, const char* sz2) { return _stricmp(sz1, sz2) == 0; }
IsEquivalentI(const fm::string & sz1,const char * sz2)544 inline bool IsEquivalentI(const fm::string& sz1, const char* sz2) { return _stricmp(sz1.c_str(), sz2) == 0; } /**< See above. */
IsEquivalentI(const char * sz1,const fm::string & sz2)545 inline bool IsEquivalentI(const char* sz1, const fm::string& sz2) { return _stricmp(sz1, sz2.c_str()) == 0; } /**< See above. */
IsEquivalentI(const fm::string & sz1,const fm::string & sz2)546 inline bool IsEquivalentI(const fm::string& sz1, const fm::string& sz2) { return _stricmp(sz1.c_str(), sz2.c_str()) == 0; } /**< See above. */
547 #ifdef UNICODE
IsEquivalentI(const fchar * sz1,const fchar * sz2)548 inline bool IsEquivalentI(const fchar* sz1, const fchar* sz2) { return fstricmp(sz1, sz2) == 0; } /**< See above. */
IsEquivalentI(const fstring & sz1,const fchar * sz2)549 inline bool IsEquivalentI(const fstring& sz1, const fchar* sz2) { return fstricmp(sz1.c_str(), sz2) == 0; } /**< See above. */
IsEquivalentI(const fchar * sz1,const fstring & sz2)550 inline bool IsEquivalentI(const fchar* sz1, const fstring& sz2) { return fstricmp(sz1, sz2.c_str()) == 0; } /**< See above. */
IsEquivalentI(const fstring & sz1,const fstring & sz2)551 inline bool IsEquivalentI(const fstring& sz1, const fstring& sz2) { return fstricmp(sz1.c_str(), sz2.c_str()) == 0; } /**< See above. */
552 #endif // UNICODE
553 
554 /** Returns whether two 8-bit strings are equivalent. This is a case-sensitive comparison.
555 	@param sz1 The first 8-bit string to compare.
556 	@param sz2 The second 8-bit string to compare.
557 	@return Whether the two 8-bit strings are equivalent. */
558 inline bool operator==(const fm::string& sz1, const char* sz2) { return strcmp(sz1.c_str(), sz2) == 0; }
559 
560 #ifdef UNICODE
561 /** Returns whether two Unicode strings are equivalent. This is a case-sensitive comparison.
562 	@param sz1 The first Unicode string to compare.
563 	@param sz2 The second Unicode string to compare.
564 	@return Whether the two Unicode strings are equivalent. */
IsEquivalent(const fchar * sz1,const fchar * sz2)565 inline bool IsEquivalent(const fchar* sz1, const fchar* sz2)
566 {
567 	return (sz1 == sz2) ? true : // ptrs same, are equivalent
568 		(sz1 == NULL || sz2 == NULL) ? // either ptr null, not equivalent
569 			false : fstrcmp(sz1, sz2) == 0; // do actual test
570 }
IsEquivalent(const fstring & sz1,const fchar * sz2)571 inline bool IsEquivalent(const fstring& sz1, const fchar* sz2) { return IsEquivalent(sz1.c_str(), sz2); } /**< See above. */
IsEquivalent(const fchar * sz1,const fstring & sz2)572 inline bool IsEquivalent(const fchar* sz1, const fstring& sz2) { return IsEquivalent(sz1, sz2.c_str()); } /**< See above. */
IsEquivalent(const fstring & sz1,const fstring & sz2)573 inline bool IsEquivalent(const fstring& sz1, const fstring& sz2) { return IsEquivalent(sz1.c_str(), sz2.c_str()); } /**< See above. */
574 
575 /** Returns whether two Unicode strings are equivalent. This is a case-sensitive comparison.
576 	@param sz1 The first Unicode string to compare.
577 	@param sz2 The second Unicode string to compare.
578 	@return Whether the two Unicode strings are equivalent. */
579 inline bool operator==(const fstring& sz1, const fchar* sz2) { return IsEquivalent(sz1.c_str(), sz2); }
580 #endif // UNICODE
581 
582 // Include the main string modification classes.
583 #include "FUtils/FUStringBuilder.h"
584 #include "FUtils/FUStringConversion.h"
585 
586 /** A Unicode string from a constant 8-bit string. */
587 #define FS(a) fstring(FC(a))
588 /** A Unicode string from any convertable value: string, vector-type or simple numeric. */
589 #define TO_FSTRING(a) FUStringConversion::ToFString(a)
590 /** An 8-bit string from any convertable value: Unicode string, vector-type or simple numeric. */
591 #define TO_STRING(a) FUStringConversion::ToString(a)
592 
593 /** An empty UTF-8 string. This string is returned in many functions when there is an error. */
594 extern FCOLLADA_EXPORT const fm::string emptyString;
595 /** An empty Unicode string. This string is returned in many functions when there is an error. */
596 extern FCOLLADA_EXPORT const fstring emptyFString;
597 
598 /** Returns whether a string builder and a string are equivalent. This is a case-sensitive comparison.
599 	@param builder The string builder to compare.
600 	@param sz The string to compare.
601 	@return Whether the two strings are equivalent. */
IsEquivalent(FUSStringBuilder & builder,const char * sz)602 inline bool IsEquivalent(FUSStringBuilder& builder, const char* sz) { return IsEquivalent(builder.ToCharPtr(), sz); }
IsEquivalent(FUSStringBuilder & builder,const fm::string & sz)603 inline bool IsEquivalent(FUSStringBuilder& builder, const fm::string& sz) { return IsEquivalent(builder.ToCharPtr(), sz.c_str()); } /**< See above. */
604 #ifdef UNICODE
IsEquivalent(FUStringBuilder & builder,const fchar * sz)605 inline bool IsEquivalent(FUStringBuilder& builder, const fchar* sz) { return IsEquivalent(builder.ToCharPtr(), sz); } /**< See above. */
IsEquivalent(FUStringBuilder & builder,const fstring & sz)606 inline bool IsEquivalent(FUStringBuilder& builder, const fstring& sz) { return IsEquivalent(builder.ToCharPtr(), sz.c_str()); } /**< See above. */
607 #endif
608 
609 #endif // _FU_STRING_H_
610