1 // Copyright (C) 2002-2005 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine" and the "irrXML" project.
3 // For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
4 
5 #ifndef __IRR_STRING_H_INCLUDED__
6 #define __IRR_STRING_H_INCLUDED__
7 
8 #include "irrTypes.h"
9 
10 namespace irr
11 {
12 namespace core
13 {
14 
15 //!	Very simple string class with some useful features.
16 /**	string<c8> and string<wchar_t> work both with unicode AND ascii,
17 so you can assign unicode to string<c8> and ascii to string<wchar_t>
18 (and the other way round) if your ever would want to.
19 Note that the conversation between both is not done using an encoding.
20 
21 Known bugs:
22 Special characters like '�', '�' and '�' are ignored in the
23 methods make_upper, make_lower and equals_ignore_case.
24 */
25 template <class T>
26 class string
27 {
28 public:
29 
30 	//! Default constructor
string()31 	string()
32 	: array(0), allocated(1), used(1)
33 	{
34 		array = new T[1];
35 		array[0] = 0x0;
36 	}
37 
38 
39 
40 	//! Constructor
string(const string<T> & other)41 	string(const string<T>& other)
42 	: array(0), allocated(0), used(0)
43 	{
44 		*this = other;
45 	}
46 
47 
48 	//! Constructs a string from an int
string(int number)49 	string(int number)
50 	: array(0), allocated(0), used(0)
51 	{
52 		// store if negative and make positive
53 
54 		bool negative = false;
55 		if (number < 0)
56 		{
57 			number *= -1;
58 			negative = true;
59 		}
60 
61 		// temporary buffer for 16 numbers
62 
63 		c8 tmpbuf[16];
64 		tmpbuf[15] = 0;
65 		s32 idx = 15;
66 
67 		// special case '0'
68 
69 		if (!number)
70 		{
71 			tmpbuf[14] = '0';
72 			*this = &tmpbuf[14];
73 			return;
74 		}
75 
76 		// add numbers
77 
78 		while(number && idx)
79 		{
80 			idx--;
81 			tmpbuf[idx] = (c8)('0' + (number % 10));
82 			number = number / 10;
83 		}
84 
85 		// add sign
86 
87 		if (negative)
88 		{
89 			idx--;
90 			tmpbuf[idx] = '-';
91 		}
92 
93 		*this = &tmpbuf[idx];
94 	}
95 
96 
97 
98 	//! Constructor for copying a string from a pointer with a given lenght
99 	template <class B>
string(const B * c,s32 lenght)100 	string(const B* c, s32 lenght)
101 	: array(0), allocated(0), used(0)
102 	{
103 		if (!c)
104 			return;
105 
106         allocated = used = lenght+1;
107 		array = new T[used];
108 
109 		for (s32 l = 0; l<lenght; ++l)
110 			array[l] = (T)c[l];
111 
112 		array[lenght] = 0;
113 	}
114 
115 
116 
117 	//! Constructor for unicode and ascii strings
118 	template <class B>
string(const B * c)119 	string(const B* c)
120 	: array(0),allocated(0), used(0)
121 	{
122 		*this = c;
123 	}
124 
125 
126 
127 	//! destructor
~string()128 	~string()
129 	{
130 		delete [] array;
131 	}
132 
133 
134 
135 	//! Assignment operator
136 	string<T>& operator=(const string<T>& other)
137 	{
138 		if (this == &other)
139 			return *this;
140 
141 		delete [] array;
142 		allocated = used = other.size()+1;
143 		array = new T[used];
144 
145 		const T* p = other.c_str();
146 		for (s32 i=0; i<used; ++i, ++p)
147 			array[i] = *p;
148 
149 		return *this;
150 	}
151 
152 
153 
154 	//! Assignment operator for strings, ascii and unicode
155 	template <class B>
156 	string<T>& operator=(const B* c)
157 	{
158 		if (!c)
159 		{
160 			if (!array)
161 			{
162 				array = new T[1];
163 				allocated = 1;
164 				used = 1;
165 			}
166 			array[0] = 0x0;
167 			return *this;
168 		}
169 
170 		if ((void*)c == (void*)array)
171 			return *this;
172 
173 		s32 len = 0;
174 		const B* p = c;
175 		while(*p)
176 		{
177 			++len;
178 			++p;
179 		}
180 
181 		// we'll take the old string for a while, because the new string could be
182 		// a part of the current string.
183 		T* oldArray = array;
184 
185         allocated = used = len+1;
186 		array = new T[used];
187 
188 		for (s32 l = 0; l<len+1; ++l)
189 			array[l] = (T)c[l];
190 
191 		delete [] oldArray;
192 		return *this;
193 	}
194 
195 	//! Add operator for other strings
196 	string<T> operator+(const string<T>& other)
197 	{
198 		string<T> str(*this);
199 		str.append(other);
200 
201 		return str;
202 	}
203 
204 	//! Add operator for strings, ascii and unicode
205 	template <class B>
206 	string<T> operator+(const B* c)
207 	{
208 		string<T> str(*this);
209 		str.append(c);
210 
211 		return str;
212 	}
213 
214 
215 
216 	//! Direct access operator
217 	T& operator [](const s32 index)  const
218 	{
219 		_IRR_DEBUG_BREAK_IF(index>=used) // bad index
220 
221 		return array[index];
222 	}
223 
224 
225 	//! Comparison operator
226 	bool operator ==(const T* str) const
227 	{
228 		int i;
229 		for(i=0; array[i] && str[i]; ++i)
230 			if (array[i] != str[i])
231 				return false;
232 
233 		return !array[i] && !str[i];
234 	}
235 
236 
237 
238 	//! Comparison operator
239 	bool operator ==(const string<T>& other) const
240 	{
241 		for(s32 i=0; array[i] && other.array[i]; ++i)
242 			if (array[i] != other.array[i])
243 				return false;
244 
245 		return used == other.used;
246 	}
247 
248 
249 
250 	//! Is smaller operator
251 	bool operator <(const string<T>& other) const
252 	{
253 		for(s32 i=0; array[i] && other.array[i]; ++i)
254 			if (array[i] != other.array[i])
255 				return (array[i] < other.array[i]);
256 
257 		return used < other.used;
258 	}
259 
260 
261 
262 	//! Equals not operator
263 	bool operator !=(const string<T>& other) const
264 	{
265 		return !(*this == other);
266 	}
267 
268 
269 
270 	//! Returns length of string
271 	/** \return Returns length of the string in characters. */
size()272 	s32 size() const
273 	{
274 		return used-1;
275 	}
276 
277 
278 
279 	//! Returns character string
280 	/** \return Returns pointer to C-style zero terminated string. */
c_str()281 	const T* c_str() const
282 	{
283 		return array;
284 	}
285 
286 
287 
288 	//! Makes the string lower case.
make_lower()289 	void make_lower()
290 	{
291 		const T A = (T)'A';
292 		const T Z = (T)'Z';
293 		const T diff = (T)'a' - A;
294 
295 		for (s32 i=0; i<used; ++i)
296 		{
297 			if (array[i]>=A && array[i]<=Z)
298 				array[i] += diff;
299 		}
300 	}
301 
302 
303 
304 	//! Makes the string upper case.
make_upper()305 	void make_upper()
306 	{
307 		const T a = (T)'a';
308 		const T z = (T)'z';
309 		const T diff = (T)'A' - a;
310 
311 		for (s32 i=0; i<used; ++i)
312 		{
313 			if (array[i]>=a && array[i]<=z)
314 				array[i] += diff;
315 		}
316 	}
317 
318 
319 
320 	//! Compares the string ignoring case.
321 	/** \param other: Other string to compare.
322 	\return Returns true if the string are equal ignoring case. */
equals_ignore_case(const string<T> & other)323 	bool equals_ignore_case(const string<T>& other) const
324 	{
325 		for(s32 i=0; array[i] && other[i]; ++i)
326 			if (toLower(array[i]) != toLower(other[i]))
327 				return false;
328 
329 		return used == other.used;
330 	}
331 
332 
333 	//! compares the first n characters of the strings
equalsn(const string<T> & other,int len)334 	bool equalsn(const string<T>& other, int len)
335 	{
336 		int i;
337 		for(i=0; array[i] && other[i] && i < len; ++i)
338 			if (array[i] != other[i])
339 				return false;
340 
341 		// if one (or both) of the strings was smaller then they
342 		// are only equal if they have the same lenght
343 		return (i == len) || (used == other.used);
344 	}
345 
346 
347 	//! compares the first n characters of the strings
equalsn(const T * str,int len)348 	bool equalsn(const T* str, int len)
349 	{
350 		int i;
351 		for(i=0; array[i] && str[i] && i < len; ++i)
352 			if (array[i] != str[i])
353 				return false;
354 
355 		// if one (or both) of the strings was smaller then they
356 		// are only equal if they have the same lenght
357 		return (i == len) || (array[i] == 0 && str[i] == 0);
358 	}
359 
360 
361 	//! Appends a character to this string
362 	/** \param character: Character to append. */
append(T character)363 	void append(T character)
364 	{
365 		if (used + 1 > allocated)
366 			reallocate((s32)used + 1);
367 
368 		used += 1;
369 
370 		array[used-2] = character;
371 		array[used-1] = 0;
372 	}
373 
374 	//! Appends a string to this string
375 	/** \param other: String to append. */
append(const string<T> & other)376 	void append(const string<T>& other)
377 	{
378 		--used;
379 
380 		s32 len = other.size();
381 
382 		if (used + len + 1 > allocated)
383 			reallocate((s32)used + (s32)len + 1);
384 
385 		for (s32 l=0; l<len+1; ++l)
386 			array[l+used] = other[l];
387 
388 		used = used + len + 1;
389 	}
390 
391 
392 	//! Appends a string of the length l to this string.
393 	/** \param other: other String to append to this string.
394 	 \param length: How much characters of the other string to add to this one. */
append(const string<T> & other,s32 length)395 	void append(const string<T>& other, s32 length)
396 	{
397 		s32 len = other.size();
398 
399 		if (len < length)
400 		{
401 			append(other);
402 			return;
403 		}
404 
405 		len = length;
406 		--used;
407 
408 		if (used + len > allocated)
409 			reallocate((s32)used + (s32)len);
410 
411 		for (s32 l=0; l<len; ++l)
412 			array[l+used] = other[l];
413 
414 		used = used + len;
415 	}
416 
417 
418 	//! Reserves some memory.
419 	/** \param count: Amount of characters to reserve. */
reserve(s32 count)420 	void reserve(s32 count)
421 	{
422 		if (count < allocated)
423 			return;
424 
425 		reallocate(count);
426 	}
427 
428 
429 	//! finds first occurrence of character in string
430 	/** \param c: Character to search for.
431 	\return Returns position where the character has been found,
432 	or -1 if not found. */
findFirst(T c)433 	s32 findFirst(T c) const
434 	{
435 		for (s32 i=0; i<used; ++i)
436 			if (array[i] == c)
437 				return i;
438 
439 		return -1;
440 	}
441 
442 	//! finds first occurrence of a character of a list in string
443 	/** \param c: List of strings to find. For example if the method
444 	should find the first occurance of 'a' or 'b', this parameter should be "ab".
445 	\param count: Amount of characters in the list. Ususally,
446 	this should be strlen(ofParameter1)
447 	\return Returns position where one of the character has been found,
448 	or -1 if not found. */
findFirstChar(T * c,int count)449 	s32 findFirstChar(T* c, int count) const
450 	{
451 		for (s32 i=0; i<used; ++i)
452 			for (int j=0; j<count; ++j)
453 				if (array[i] == c[j])
454 					return i;
455 
456 		return -1;
457 	}
458 
459 
460 	//! Finds first position of a character not in a given list.
461 	/** \param c: List of characters not to find. For example if the method
462 	 should find the first occurance of a character not 'a' or 'b', this parameter should be "ab".
463 	\param count: Amount of characters in the list. Ususally,
464 	this should be strlen(ofParameter1)
465 	\return Returns position where the character has been found,
466 	or -1 if not found. */
467 	template <class B>
findFirstCharNotInList(B * c,int count)468 	s32 findFirstCharNotInList(B* c, int count) const
469 	{
470 		for (int i=0; i<used; ++i)
471 		{
472             int j;
473 			for (j=0; j<count; ++j)
474 				if (array[i] == c[j])
475 					break;
476 
477 			if (j==count)
478 				return i;
479 		}
480 
481 		return -1;
482 	}
483 
484 	//! Finds last position of a character not in a given list.
485 	/** \param c: List of characters not to find. For example if the method
486 	 should find the first occurance of a character not 'a' or 'b', this parameter should be "ab".
487 	\param count: Amount of characters in the list. Ususally,
488 	this should be strlen(ofParameter1)
489 	\return Returns position where the character has been found,
490 	or -1 if not found. */
491 	template <class B>
findLastCharNotInList(B * c,int count)492 	s32 findLastCharNotInList(B* c, int count) const
493 	{
494 		for (int i=used-2; i>=0; --i)
495 		{
496             int j;
497 			for (j=0; j<count; ++j)
498 				if (array[i] == c[j])
499 					break;
500 
501 			if (j==count)
502 				return i;
503 		}
504 
505 		return -1;
506 	}
507 
508 	//! finds next occurrence of character in string
509 	/** \param c: Character to search for.
510 	\param startPos: Position in string to start searching.
511 	\return Returns position where the character has been found,
512 	or -1 if not found. */
findNext(T c,s32 startPos)513 	s32 findNext(T c, s32 startPos) const
514 	{
515 		for (s32 i=startPos; i<used; ++i)
516 			if (array[i] == c)
517 				return i;
518 
519 		return -1;
520 	}
521 
522 
523 	//! finds last occurrence of character in string
524 	//! \param c: Character to search for.
525 	//! \return Returns position where the character has been found,
526 	//! or -1 if not found.
findLast(T c)527 	s32 findLast(T c) const
528 	{
529 		for (s32 i=used-1; i>=0; --i)
530 			if (array[i] == c)
531 				return i;
532 
533 		return -1;
534 	}
535 
536 
537 	//! Returns a substring
538 	//! \param begin: Start of substring.
539 	//! \param length: Length of substring.
subString(s32 begin,s32 length)540 	string<T> subString(s32 begin, s32 length)
541 	{
542 		if (length <= 0)
543 			return string<T>("");
544 
545 		string<T> o;
546 		o.reserve(length+1);
547 
548 		for (s32 i=0; i<length; ++i)
549 			o.array[i] = array[i+begin];
550 
551 		o.array[length] = 0;
552 		o.used = o.allocated;
553 
554 		return o;
555 	}
556 
557 
558 	void operator += (T c)
559 	{
560 		append(c);
561 	}
562 
563 	void operator += (const string<T>& other)
564 	{
565 		append(other);
566 	}
567 
568 	void operator += (int i)
569 	{
570 		append(string<T>(i));
571 	}
572 
573 	//! replaces all characters of a special type with another one
replace(T toReplace,T replaceWith)574 	void replace(T toReplace, T replaceWith)
575 	{
576 		for (s32 i=0; i<used; ++i)
577 			if (array[i] == toReplace)
578 				array[i] = replaceWith;
579 	}
580 
581 	//! trims the string.
582 	/** Removes whitespace from begin and end of the string. */
trim()583 	void trim()
584 	{
585 		const char whitespace[] = " \t\n";
586 		const int whitespacecount = 3;
587 
588 		// find start and end of real string without whitespace
589 		int begin = findFirstCharNotInList(whitespace, whitespacecount);
590 		if (begin == -1)
591 			return;
592 
593 		int end = findLastCharNotInList(whitespace, whitespacecount);
594 		if (end == -1)
595 			return;
596 
597 		*this = subString(begin, (end +1) - begin);
598 	}
599 
600 
601 	//! Erases a character from the string. May be slow, because all elements
602 	//! following after the erased element have to be copied.
603 	//! \param index: Index of element to be erased.
erase(int index)604 	void erase(int index)
605 	{
606 		_IRR_DEBUG_BREAK_IF(index>=used || index<0) // access violation
607 
608 		for (int i=index+1; i<used; ++i)
609 			array[i-1] = array[i];
610 
611 		--used;
612 	}
613 
614 
615 
616 private:
617 
618 	//! Returns a character converted to lower case
toLower(const T & t)619 	T toLower(const T& t) const
620 	{
621 		if (t>=(T)'A' && t<=(T)'Z')
622 			return t + ((T)'a' - (T)'A');
623 		else
624 			return t;
625 	}
626 
627 	//! Reallocate the array, make it bigger or smaler
reallocate(s32 new_size)628 	void reallocate(s32 new_size)
629 	{
630 		T* old_array = array;
631 
632 		array = new T[new_size];
633 		allocated = new_size;
634 
635 		s32 amount = used < new_size ? used : new_size;
636 		for (s32 i=0; i<amount; ++i)
637 			array[i] = old_array[i];
638 
639 		if (allocated < used)
640 			used = allocated;
641 
642 		delete [] old_array;
643 	}
644 
645 
646 	//--- member variables
647 
648 	T* array;
649 	s32 allocated;
650 	s32 used;
651 };
652 
653 
654 //! Typedef for character strings
655 typedef string<irr::c8> stringc;
656 
657 //! Typedef for wide character strings
658 typedef string<wchar_t> stringw;
659 
660 } // end namespace core
661 } // end namespace irr
662 
663 #endif
664 
665