1 // This code is in the public domain -- castanyo@yahoo.es
2
3 #include <nvcore/StrLib.h>
4
5 #include <math.h> // log
6 #include <stdio.h> // vsnprintf
7
8 #if NV_CC_MSVC
9 #include <stdarg.h> // vsnprintf
10 #endif
11
12 #if NV_OS_WIN32
13 #define NV_PATH_SEPARATOR '\\'
14 #else
15 #define NV_PATH_SEPARATOR '/'
16 #endif
17
18 using namespace nv;
19
20 namespace
21 {
strAlloc(uint size)22 static char * strAlloc(uint size)
23 {
24 return static_cast<char *>(::malloc(size));
25 }
26
strReAlloc(char * str,uint size)27 static char * strReAlloc(char * str, uint size)
28 {
29 return static_cast<char *>(::realloc(str, size));
30 }
31
strFree(const char * str)32 static void strFree(const char * str)
33 {
34 return ::free(const_cast<char *>(str));
35 }
36
37 /*static char * strDup( const char * str )
38 {
39 nvDebugCheck( str != NULL );
40 uint len = uint(strlen( str ) + 1);
41 char * dup = strAlloc( len );
42 memcpy( dup, str, len );
43 return dup;
44 }*/
45
46 // helper function for integer to string conversion.
i2a(uint i,char * a,uint r)47 static char * i2a( uint i, char *a, uint r )
48 {
49 if( i / r > 0 ) {
50 a = i2a( i / r, a, r );
51 }
52 *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % r];
53 return a + 1;
54 }
55
56 // Locale independent functions.
toUpper(char c)57 static inline char toUpper( char c ) {
58 return (c<'a' || c>'z') ? (c) : (c+'A'-'a');
59 }
toLower(char c)60 static inline char toLower( char c ) {
61 return (c<'A' || c>'Z') ? (c) : (c+'a'-'A');
62 }
isAlpha(char c)63 static inline bool isAlpha( char c ) {
64 return (c>='a' && c<='z') || (c>='A' && c<='Z');
65 }
isDigit(char c)66 static inline bool isDigit( char c ) {
67 return c>='0' && c<='9';
68 }
isAlnum(char c)69 static inline bool isAlnum( char c ) {
70 return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
71 }
72
73 }
74
strCmp(const char * s1,const char * s2)75 int nv::strCmp(const char * s1, const char * s2)
76 {
77 nvDebugCheck(s1 != NULL);
78 nvDebugCheck(s2 != NULL);
79 return strcmp(s1, s2);
80 }
81
strCaseCmp(const char * s1,const char * s2)82 int nv::strCaseCmp(const char * s1, const char * s2)
83 {
84 nvDebugCheck(s1 != NULL);
85 nvDebugCheck(s1 != NULL);
86 #if NV_CC_MSVC
87 return _stricmp(s1, s2);
88 #else
89 return strcasecmp(s1, s2);
90 #endif
91 }
92
strCpy(char * dst,int size,const char * src)93 void nv::strCpy(char * dst, int size, const char * src)
94 {
95 nvDebugCheck(dst != NULL);
96 nvDebugCheck(src != NULL);
97 #if NV_CC_MSVC && _MSC_VER >= 1400
98 strcpy_s(dst, size, src);
99 #else
100 NV_UNUSED(size);
101 strcpy(dst, src);
102 #endif
103 }
104
strCpy(char * dst,int size,const char * src,int len)105 void nv::strCpy(char * dst, int size, const char * src, int len)
106 {
107 nvDebugCheck(dst != NULL);
108 nvDebugCheck(src != NULL);
109 #if NV_CC_MSVC && _MSC_VER >= 1400
110 strncpy_s(dst, size, src, len);
111 #else
112 NV_UNUSED(size);
113 strncpy(dst, src, len);
114 #endif
115 }
116
strCat(char * dst,int size,const char * src)117 void nv::strCat(char * dst, int size, const char * src)
118 {
119 nvDebugCheck(dst != NULL);
120 nvDebugCheck(src != NULL);
121 #if NV_CC_MSVC && _MSC_VER >= 1400
122 strcat_s(dst, size, src);
123 #else
124 NV_UNUSED(size);
125 strcat(dst, src);
126 #endif
127 }
128
129
130 /** Pattern matching routine. I don't remember where did I get this. */
strMatch(const char * str,const char * pat)131 bool nv::strMatch(const char * str, const char * pat)
132 {
133 nvDebugCheck(str != NULL);
134 nvDebugCheck(pat != NULL);
135
136 char c2;
137
138 while (true) {
139 if (*pat==0) {
140 if (*str==0) return true;
141 else return false;
142 }
143 if ((*str==0) && (*pat!='*')) return false;
144 if (*pat=='*') {
145 pat++;
146 if (*pat==0) return true;
147 while (true) {
148 if (strMatch(str, pat)) return true;
149 if (*str==0) return false;
150 str++;
151 }
152 }
153 if (*pat=='?') goto match;
154 if (*pat=='[') {
155 pat++;
156 while (true) {
157 if ((*pat==']') || (*pat==0)) return false;
158 if (*pat==*str) break;
159 if (pat[1] == '-') {
160 c2 = pat[2];
161 if (c2==0) return false;
162 if ((*pat<=*str) && (c2>=*str)) break;
163 if ((*pat>=*str) && (c2<=*str)) break;
164 pat+=2;
165 }
166 pat++;
167 }
168 while (*pat!=']') {
169 if (*pat==0) {
170 pat--;
171 break;
172 }
173 pat++;
174 }
175 goto match;
176 }
177
178 if (*pat == NV_PATH_SEPARATOR) {
179 pat++;
180 if (*pat==0) return false;
181 }
182 if (*pat!=*str) return false;
183
184 match:
185 pat++;
186 str++;
187 }
188 }
189
190
191
192 /** Empty string. */
StringBuilder()193 StringBuilder::StringBuilder() : m_size(0), m_str(NULL)
194 {
195 }
196
197 /** Preallocate space. */
StringBuilder(int size_hint)198 StringBuilder::StringBuilder( int size_hint ) : m_size(size_hint)
199 {
200 nvDebugCheck(m_size > 0);
201 m_str = strAlloc(m_size);
202 *m_str = '\0';
203 }
204
205 /** Copy ctor. */
StringBuilder(const StringBuilder & s)206 StringBuilder::StringBuilder( const StringBuilder & s ) : m_size(0), m_str(NULL)
207 {
208 copy(s);
209 }
210
211 /** Copy string. */
StringBuilder(const char * s)212 StringBuilder::StringBuilder( const char * s ) : m_size(0), m_str(NULL)
213 {
214 copy(s);
215 }
216
217 /** Delete the string. */
~StringBuilder()218 StringBuilder::~StringBuilder()
219 {
220 m_size = 0;
221 strFree(m_str);
222 m_str = NULL;
223 }
224
225
226 /** Format a string safely. */
format(const char * fmt,...)227 StringBuilder & StringBuilder::format( const char * fmt, ... )
228 {
229 nvDebugCheck(fmt != NULL);
230 va_list arg;
231 va_start( arg, fmt );
232
233 format( fmt, arg );
234
235 va_end( arg );
236
237 return *this;
238 }
239
240
241 /** Format a string safely. */
format(const char * fmt,va_list arg)242 StringBuilder & StringBuilder::format( const char * fmt, va_list arg )
243 {
244 nvDebugCheck(fmt != NULL);
245
246 if( m_size == 0 ) {
247 m_size = 64;
248 m_str = strAlloc( m_size );
249 }
250
251 va_list tmp;
252 va_copy(tmp, arg);
253 #if NV_CC_MSVC && _MSC_VER >= 1400
254 int n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp);
255 #else
256 int n = vsnprintf(m_str, m_size, fmt, tmp);
257 #endif
258 va_end(tmp);
259
260 while( n < 0 || n >= int(m_size) ) {
261 if( n > -1 ) {
262 m_size = n + 1;
263 }
264 else {
265 m_size *= 2;
266 }
267
268 m_str = strReAlloc(m_str, m_size);
269
270 va_copy(tmp, arg);
271 #if NV_CC_MSVC && _MSC_VER >= 1400
272 n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp);
273 #else
274 n = vsnprintf(m_str, m_size, fmt, tmp);
275 #endif
276 va_end(tmp);
277 }
278
279 nvDebugCheck(n < int(m_size));
280
281 // Make sure it's null terminated.
282 nvDebugCheck(m_str[n] == '\0');
283 //str[n] = '\0';
284
285 return *this;
286 }
287
288
289 /** Append a string. */
append(const char * s)290 StringBuilder & StringBuilder::append( const char * s )
291 {
292 nvDebugCheck(s != NULL);
293
294 const uint slen = uint(strlen( s ));
295
296 if( m_str == NULL ) {
297 m_size = slen + 1;
298 m_str = strAlloc(m_size);
299 strCpy( m_str, m_size, s );
300 }
301 else {
302
303 const uint len = uint(strlen( m_str ));
304
305 if( m_size < len + slen + 1 ) {
306 m_size = len + slen + 1;
307 m_str = strReAlloc(m_str, m_size);
308 }
309
310 strCat( m_str, m_size, s );
311 }
312
313 return *this;
314 }
315
316
317 /** Append a formatted string. */
appendFormat(const char * format,...)318 StringBuilder & StringBuilder::appendFormat( const char * format, ... )
319 {
320 nvDebugCheck( format != NULL );
321
322 va_list arg;
323 va_start( arg, format );
324
325 appendFormat( format, arg );
326
327 va_end( arg );
328
329 return *this;
330 }
331
332
333 /** Append a formatted string. */
appendFormat(const char * format,va_list arg)334 StringBuilder & StringBuilder::appendFormat( const char * format, va_list arg )
335 {
336 nvDebugCheck( format != NULL );
337
338 va_list tmp;
339 va_copy(tmp, arg);
340
341 StringBuilder tmp_str;
342 tmp_str.format( format, tmp );
343 append( tmp_str );
344
345 va_end(tmp);
346
347 return *this;
348 }
349
350
351 /** Convert number to string in the given base. */
number(int i,int base)352 StringBuilder & StringBuilder::number( int i, int base )
353 {
354 nvCheck( base >= 2 );
355 nvCheck( base <= 36 );
356
357 // @@ This needs to be done correctly.
358 // length = floor(log(i, base));
359 uint len = uint(log(float(i)) / log(float(base)) + 2); // one more if negative
360 reserve(len);
361
362 if( i < 0 ) {
363 *m_str = '-';
364 *i2a(uint(-i), m_str+1, base) = 0;
365 }
366 else {
367 *i2a(i, m_str, base) = 0;
368 }
369
370 return *this;
371 }
372
373
374 /** Convert number to string in the given base. */
number(uint i,int base)375 StringBuilder & StringBuilder::number( uint i, int base )
376 {
377 nvCheck( base >= 2 );
378 nvCheck( base <= 36 );
379
380 // @@ This needs to be done correctly.
381 // length = floor(log(i, base));
382 uint len = uint(log(float(i)) / log(float(base)) - 0.5f + 1);
383 reserve(len);
384
385 *i2a(i, m_str, base) = 0;
386
387 return *this;
388 }
389
390
391 /** Resize the string preserving the contents. */
reserve(uint size_hint)392 StringBuilder & StringBuilder::reserve( uint size_hint )
393 {
394 nvCheck(size_hint != 0);
395 if( size_hint > m_size ) {
396 m_str = strReAlloc(m_str, size_hint);
397 m_size = size_hint;
398 }
399 return *this;
400 }
401
402
403 /** Copy a string safely. */
copy(const char * s)404 StringBuilder & StringBuilder::copy( const char * s )
405 {
406 nvCheck( s != NULL );
407 uint str_size = uint(strlen( s )) + 1;
408 reserve(str_size);
409 strCpy( m_str, str_size, s );
410 return *this;
411 }
412
413
414 /** Copy an StringBuilder. */
copy(const StringBuilder & s)415 StringBuilder & StringBuilder::copy( const StringBuilder & s )
416 {
417 if( s.m_str == NULL ) {
418 nvCheck( s.m_size == 0 );
419 m_size = 0;
420 strFree( m_str );
421 m_str = NULL;
422 }
423 else {
424 reserve( s.m_size );
425 strCpy( m_str, s.m_size, s.m_str );
426 }
427 return *this;
428 }
429
430 /** Reset the string. */
reset()431 void StringBuilder::reset()
432 {
433 m_size = 0;
434 strFree( m_str );
435 m_str = NULL;
436 }
437
438
439 /// Get the file name from a path.
fileName() const440 const char * Path::fileName() const
441 {
442 return fileName(m_str);
443 }
444
445
446 /// Get the extension from a file path.
extension() const447 const char * Path::extension() const
448 {
449 return extension(m_str);
450 }
451
452
453 /// Toggles path separators (ie. \\ into /).
translatePath()454 void Path::translatePath()
455 {
456 nvCheck( m_str != NULL );
457
458 for(int i = 0; ; i++) {
459 if( m_str[i] == '\0' ) break;
460 #if NV_PATH_SEPARATOR == '/'
461 if( m_str[i] == '\\' ) m_str[i] = NV_PATH_SEPARATOR;
462 #else
463 if( m_str[i] == '/' ) m_str[i] = NV_PATH_SEPARATOR;
464 #endif
465 }
466 }
467
468
469 /**
470 * Strip the file name from a path.
471 * @warning path cannot end with '/' o '\\', can't it?
472 */
stripFileName()473 void Path::stripFileName()
474 {
475 nvCheck( m_str != NULL );
476
477 int length = (int)strlen(m_str) - 1;
478 while (length > 0 && m_str[length] != '/' && m_str[length] != '\\'){
479 length--;
480 }
481 if( length ) {
482 m_str[length+1] = 0;
483 }
484 else {
485 m_str[0] = 0;
486 }
487 }
488
489
490 /// Strip the extension from a path name.
stripExtension()491 void Path::stripExtension()
492 {
493 nvCheck( m_str != NULL );
494
495 int length = (int)strlen(m_str) - 1;
496 while( length > 0 && m_str[length] != '.' ) {
497 length--;
498 if( m_str[length] == NV_PATH_SEPARATOR ) {
499 return; // no extension
500 }
501 }
502 if( length ) {
503 m_str[length] = 0;
504 }
505 }
506
507
508 /// Get the path separator.
509 // static
separator()510 char Path::separator()
511 {
512 return NV_PATH_SEPARATOR;
513 }
514
515 // static
fileName(const char * str)516 const char * Path::fileName(const char * str)
517 {
518 nvCheck( str != NULL );
519
520 int length = (int)strlen(str) - 1;
521 while( length >= 0 && str[length] != separator() ) {
522 length--;
523 }
524
525 return &str[length+1];
526 }
527
528 // static
extension(const char * str)529 const char * Path::extension(const char * str)
530 {
531 nvCheck( str != NULL );
532
533 int length, l;
534 l = length = (int)strlen( str );
535 while( length > 0 && str[length] != '.' ) {
536 length--;
537 if( str[length] == separator() ) {
538 return &str[l]; // no extension
539 }
540 }
541 if( length == 0 ) {
542 return &str[l];
543 }
544 return &str[length];
545 }
546
547
548
549 /// Clone this string
clone() const550 String String::clone() const
551 {
552 String str(data);
553 return str;
554 }
555
setString(const char * str)556 void String::setString(const char * str)
557 {
558 if (str == NULL) {
559 data = NULL;
560 }
561 else {
562 allocString( str );
563 addRef();
564 }
565 }
566
setString(const char * str,int length)567 void String::setString(const char * str, int length)
568 {
569 nvDebugCheck(str != NULL);
570
571 allocString(str, length);
572 addRef();
573 }
574
setString(const StringBuilder & str)575 void String::setString(const StringBuilder & str)
576 {
577 if (str.str() == NULL) {
578 data = NULL;
579 }
580 else {
581 allocString(str);
582 addRef();
583 }
584 }
585