HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SYS_String.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: SYS_String.h (SYS Library, C++)
7  *
8  * COMMENTS:
9  * System-independent string manipulation functions.
10  */
11 
12 #ifndef __SYS_String__
13 #define __SYS_String__
14 
15 #include "SYS_API.h"
16 
17 #include "SYS_Inline.h"
18 #include "SYS_Types.h"
19 #include "SYS_StaticAssert.h"
20 
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 static inline constexpr uint32
26 SYSstring_hashseed(
27  const char *str,
29  uint32 hash = 0,
30  bool allow_nulls = false)
31 {
32  if (!str || length <= 0 || (!allow_nulls && *str == '\0'))
33  return hash;
34 
35  // A note on the magic number 37.
36  // We want to scale by SOMETHING so that order is preserved.
37  // That something should be prime and not a power of two to
38  // avoid wrapping issues.
39  // That something should be larger than our range of expected
40  // values to avoid interference between consecutive letters.
41  // 0-9a-z is 36 letters long.
42  //
43  // The real reason is that this is what Perl uses.
44  if (!allow_nulls)
45  {
46  for (; length > 0 && *str != '\0'; length--, str++)
47  {
48  hash = (37 * hash) + (*str);
49  }
50  }
51  else
52  {
53  for (; length > 0; length--, str++)
54  {
55  hash = (37 * hash) + (*str);
56  }
57  }
58 
59  // Make sure we never return zero for non-zero hash, since in many
60  // cases we precompute string hashes and zero means "not initialized".
61  // This allows us conforming values across different string
62  // container implementations.
63  if (hash == 0)
64  hash = 1;
65 
66  return hash;
67 }
68 
69 /// Generate a hash for a char string
70 static inline constexpr uint32
71 SYSstring_hash(
72  const char *str,
73  exint len = SYS_EXINT_MAX,
74  bool allow_nulls = false)
75 {
76  return SYSstring_hashseed(str, len, /*seed*/ 0, allow_nulls);
77 }
78 
79 template <typename T>
80 static inline constexpr uint32
81 SYSfnv1a_hash(const T *str, exint len = -1, uint32 hash = 2166136261u)
82 {
83  SYS_STATIC_ASSERT_MSG(sizeof(T) == 1, "fnv1 only works on byte sized types");
84  constexpr uint32 MUL = 16777619u;
85  if (len >= 0)
86  {
87  for (exint i = 0; i < len; ++i)
88  hash = (hash ^ static_cast<uchar>(str[i])) * MUL;
89  }
90  else
91  {
92  if (str)
93  {
94  for (exint i = 0; str[i]; ++i)
95  hash = (hash ^ static_cast<uchar>(str[i])) * MUL;
96  }
97  }
98  return hash ? hash : 1;
99 }
100 
101 template <typename T>
102 static inline constexpr uint64
103 SYSfnv1a_hash64(const T *str, exint len = -1, uint64 hash = 0xcbf29ce484222325ull)
104 {
105  SYS_STATIC_ASSERT_MSG(sizeof(T) == 1, "fnv1 only works on byte sized types");
106  constexpr uint64 MUL = 0x00000100000001b3ull;
107  if (len >= 0)
108  {
109  for (exint i = 0; i < len; ++i)
110  hash = (hash ^ static_cast<uchar>(str[i])) * MUL;
111  }
112  else
113  {
114  if (str)
115  {
116  for (exint i = 0; str[i]; ++i)
117  hash = (hash ^ static_cast<uchar>(str[i])) * MUL;
118  }
119  }
120  return hash ? hash : 1;
121 }
122 
123 /// A standard name for a strtok that doesn't maintain state between calls.
124 /// This version is thus both reentrant and threadsafe.
125 /// SYSstrtok parses a string into a sequence of tokens. On the first call to
126 /// SYSstrtok, the string to be parsed must be specified as the parameter
127 /// 'string'. This parameter *will be modified* (destroying your copy).
128 /// 'delimit' specifies an array of single characters that will be used
129 /// as delimiters.
130 /// 'context' is a char * variable used internally by SYSstrtok to maintain
131 /// context between calls. Subsequent calls must specify the same unchanged
132 /// context variable as the first call.
133 /// To use SYSstrtok, on the first call first pass in your string as the
134 /// parameter 'string'; on subsequent calls, pass it in as nullptr.
135 /// SYSstrtok returns non-empty strings pointing to the first non-delimiter
136 /// character of each token, or nullptr if no further tokens are available.
137 /// Example:
138 /// @code
139 /// char *string = strdup(getString());
140 /// char *strptr = string;
141 /// char *context;
142 /// char *token = SYSstrtok(string, MY_DELIMITERS, &context);
143 /// while (token)
144 /// {
145 /// do_some_stuff();
146 /// SYSstrtok(nullptr, MY_DELIMITERS, &context);
147 /// }
148 /// free(strptr);
149 /// @endcode
150 inline char *
151 SYSstrtok(char *string, const char *delimit, char **context)
152 {
153 #ifdef LINUX
154  return strtok_r(string, delimit, context);
155 #else
156  // MSVC 2003 doesn't have strtok_r. 2005 has strtok_s, which is the same
157  // as strtok_r. Until we upgrade, use this C version of strtok_r.
158  if (string == nullptr)
159  {
160  string = *context;
161  }
162 
163  // Find and skip any leading delimiters.
164  string += strspn(string, delimit);
165 
166  // There are only delimiters (or no text at all), so we've reached the end
167  // of the string.
168  if (*string == '\0')
169  {
170  *context = string;
171  return nullptr;
172  }
173 
174  // String now points at a token.
175  char *token = string;
176 
177  // Find the end of the token.
178  string = strpbrk(token, delimit);
179  if (!string)
180  {
181  // This token is at the end of the string. Set the context to point at
182  // the end of the string so on the next call, we'll return nullptr.
183  *context = strchr(token, '\0');
184  }
185  else
186  {
187  // This is a token somewhere in the string. Set the found delimiter to
188  // zero and initialize the context to the next character.
189  *string = '\0';
190  *context = string + 1;
191  }
192 
193  return token;
194 #endif
195 }
196 
197 /// The semantics for strncpy() leave a little to be desired
198 /// - If the buffer limit is hit, the string isn't guaranteed to be null
199 /// terminated.
200 /// - If the buffer limit isn't hit, the entire remainder of the string is
201 /// filled with nulls (which can be costly with large buffers).
202 /// The following implements the strlcpy() function from OpenBSD. The function
203 /// is very similar to strncpy() but
204 /// The return code is the length of the src string
205 /// The resulting string is always null terminated (unless size == 0)
206 /// The remaining buffer is not touched
207 /// It's possible to check for errors by testing rcode >= size.
208 ///
209 /// The size is the size of the buffer, not the portion of the sub-string to
210 /// copy. If you want to only copy a portion of a string, make sure that the
211 /// @c size passed in is one @b larger than the length of the string since
212 /// SYSstrlcpy() will always ensure the string is null terminated.
213 ///
214 /// It is invalid to pass a size of 0.
215 ///
216 /// Examples: @code
217 /// char buf[8];
218 /// strncpy(buf, "dog", 8) // buf == ['d','o','g',0,0,0,0,0]
219 /// SYSstrlcpy(buf, "dog", 8) // buf == ['d','o','g',0,?,?,?,?]
220 /// strncpy(buf, "dog", 2) // buf == ['d','o',0,0,0,0,0,0]
221 /// SYSstrlcpy(buf, "dog", 2) // buf == ['d',0,?,?,?,?,?,?]
222 /// SYSstrlcpy(buf, "dog", 3) // buf == ['d','o',0,?,?,?,?]
223 /// @endcode
224 inline size_t
225 SYSstrlcpy(char *dest, const char *src, size_t size)
226 {
227  char *end = (char *)::memccpy(dest, src, 0, size);
228  if (end)
229  {
230  return end - dest - 1;
231  }
232  // No null terminator found in the first size bytes
233  if (size)
234  dest[size-1] = 0;
235 
236  // Return rcode >= size to indicate that we would've busted the buffer.
237  return size + 1;
238 }
239 
240 /// The following implements the strlcpy() function from OpenBSD. The
241 /// differences between strlcpy() and strncpy() are:
242 /// - The buffer will not be filled with null
243 /// - The size passed in is the full length of the buffer (not
244 /// remaining length)
245 /// - The dest will always be null terminated (unless it is already larger
246 /// than the size passed in)
247 /// The function returns strln(src) + SYSmin(size, strlen(dest))
248 /// If rcode >= size, truncation occurred
249 inline size_t
250 SYSstrlcat(char *dest, const char *src, size_t size)
251 {
252  // Find the length of the dest buffer. Only check for a null within the
253  // allocated space of the buffer (i.e. we can't use strlen()).
254  size_t dlen;
255  for (dlen = 0; dlen < size; dlen++)
256  if (!dest[dlen])
257  break;
258  if (dlen == size)
259  return size + 1; // Not enough space left
260  // Now, copy the source over
261  return dlen + SYSstrlcpy(dest+dlen, src, size-dlen);
262 }
263 
264 inline int
265 SYSstrcasecmp(const char *a, const char *b)
266 {
267  // Properly compare null strings, matching UT_String.
268  if (!a || !b)
269  {
270  if (a) return 1;
271  if (b) return -1;
272  return 0;
273  }
274 #if defined(WIN32)
275  return ::stricmp(a, b);
276 #else
277  return ::strcasecmp(a, b);
278 #endif
279 }
280 
281 inline int
282 SYSstrcmp(const char *a, const char *b)
283 {
284  // Properly compare null strings, matching UT_String.
285  if (!a || !b)
286  {
287  if (a) return 1;
288  if (b) return -1;
289  return 0;
290  }
291  return ::strcmp(a, b);
292 }
293 
294 #define WRAP_NULLTEST_C(FUNCTION, CONST) \
295 inline CONST char * \
296 SYS##FUNCTION(CONST char *s, int c) \
297 { \
298  if (!s) return nullptr; \
299  return ::FUNCTION(s, c); \
300 } \
301 /**/
302 
303 #define WRAP_NULLTEST(FUNCTION) \
304 WRAP_NULLTEST_C(FUNCTION, ) \
305 WRAP_NULLTEST_C(FUNCTION, const) \
306 /**/
307 
308 // The standard does not specify behaviour on null strings, but it
309 // is reasonable to say a search token is never inside a null string,
310 // thereby increasing safety.
311 WRAP_NULLTEST(strchr)
312 WRAP_NULLTEST(strrchr)
313 
314 #undef WRAP_NULLTEST
315 #undef WRAP_NULLTEST_C
316 
317 inline int
318 SYSstrncasecmp(const char *a, const char *b, size_t n)
319 {
320  if (!a || !b)
321  {
322  if (a) return 1;
323  if (b) return -1;
324  return 0;
325  }
326 #if defined(WIN32)
327  return ::strnicmp(a, b, n);
328 #else
329  return ::strncasecmp(a, b, n);
330 #endif
331 }
332 
333 /// Replacement for strcasestr, since no equivalent exists on Win32.
334 inline char *
335 SYSstrcasestr(const char *haystack, const char *needle)
336 {
337 #if defined(WIN32)
338  // Designed for the normal case (small needle, large haystack).
339  // Asymptotic cases will probably perform very poorly. For those, we'll
340  // need: https://en.wikipedia.org/wiki/Boyer-Moore-Horspool_algorithm
341  if (!haystack || !needle)
342  return nullptr;
343 
344  // Empty needle gives beginning of string.
345  if (!*needle)
346  return const_cast<char *>(haystack);
347  for(;;)
348  {
349  // Find the start of the pattern in the string.
350  while(*haystack && tolower(*haystack) != tolower(*needle))
351  haystack++;
352 
353  if (!*haystack)
354  return nullptr;
355 
356  // Found the start of the pattern.
357  const char *h = haystack, *n = needle;
358  do
359  {
360  // End of needle? We found our man.
361  if (!*++n)
362  return const_cast<char *>(haystack);
363  // End of haystack? Nothing more to look for.
364  if (!*++h)
365  return nullptr;
366  } while(tolower(*h) == tolower(*n));
367 
368  haystack++;
369  }
370 #else
371  return const_cast<char*>(::strcasestr(const_cast<char*>(haystack),needle));
372 #endif
373 }
374 
375 // Implementation of strndup for Windows.
376 inline char *
377 SYSstrndup(const char *s, size_t n)
378 {
379 #if defined(WIN32)
380  size_t l = ::strlen(s);
381  if (l < n) n = l;
382  char *r = (char *)::malloc(n + 1);
383  ::memcpy(r, s, n);
384  r[n] = '\0';
385  return r;
386 #else
387  return ::strndup(s, n);
388 #endif
389 }
390 
391 // On Windows, is*() methods are badly implemented.
392 // Running testut -i -t SYS_String shows about at least a 1.3x speed up.
393 #ifdef _WIN32
394 SYS_FORCE_INLINE bool
395 SYSisalpha(unsigned char c)
396 {
397  // This test relies on promoting to unsigned integer
398  return (unsigned(c & ~(1<<5)) - 'A') <= ('Z' - 'A');
399 }
400 SYS_FORCE_INLINE bool
401 SYSisdigit(unsigned char c)
402 {
403  // Interestingly, this tends to perform better than one comparison
404  return (c >= '0' && c <= '9');
405 }
406 #endif // _WIN32
407 
408 // From time to time, we run into problems with the locale changing
409 // unexpectedly on us leading to things like SYSisprint(227) returning true
410 // when we're in the en_CA.UTF-8 LC_CTYPE locale on macOS. Since everything
411 // assumes we're in the C locale, hardcode the range explicitly.
412 SYS_FORCE_INLINE bool
413 SYSisprint(unsigned char c)
414 {
415  return ( c >= static_cast<unsigned char>(32)
416  && c < static_cast<unsigned char>(127));
417 }
418 
419 // Windows decided in their infinite wisdom that negative values
420 // should crash their isfoo() functions, guard by only taking unsigned char
421 // arguments which get casted again to int's.
422 
423 #define SYS_IS_WRAPPER(TEST) \
424 SYS_FORCE_INLINE bool \
425 SYS##TEST(unsigned char c) \
426 { \
427  return TEST(c); \
428 } \
429 /**/
430 
431 SYS_IS_WRAPPER(isalnum)
432 #ifndef _WIN32
434 #endif
435 // isascii is specifically marked deprecated
436 // SYS_IS_WRAPPER(isascii)
437 // This does have a POSIX standard, but isn't in Windows.
438 // SYS_IS_WRAPPER(isblank)
439 SYS_IS_WRAPPER(iscntrl)
440 #ifndef _WIN32
441 SYS_IS_WRAPPER(isdigit)
442 #endif
443 SYS_IS_WRAPPER(isgraph)
444 SYS_IS_WRAPPER(islower)
445 //SYS_IS_WRAPPER(isprint) // see above
446 SYS_IS_WRAPPER(ispunct)
447 // isspace is rather important we get very, very, fast.
448 // SYS_IS_WRAPPER(isspace)
449 SYS_IS_WRAPPER(isupper)
450 SYS_IS_WRAPPER(isxdigit)
451 
452 #undef SYS_IS_WRAPPER
453 
454  static SYS_FORCE_INLINE const void *
455  SYSmemrchr(const void *v, int c, exint n)
456  {
457 #if defined(LINUX)
458  return ::memrchr(v, c, n);
459 #else
460  const unsigned char *beg = (const unsigned char *)v;
461  const unsigned char *full_end = (const unsigned char *)v + n;
462  const unsigned char *end = (const unsigned char *)v + (n/4)*4;
463  for (const unsigned char *s = full_end; s-->end;)
464  {
465  if (*s == c)
466  return s;
467  }
468  for (const unsigned char *s = end-1; s > beg;)
469  {
470  if (*s == c) return s;
471  --s;
472  if (*s == c) return s;
473  --s;
474  if (*s == c) return s;
475  --s;
476  if (*s == c) return s;
477  --s;
478  }
479  return nullptr;
480 #endif
481  }
482 
483 static SYS_FORCE_INLINE constexpr int
484 SYSmemcmp(const void *lhs, const void *rhs, size_t count)
485 {
486  return __builtin_memcmp(lhs, rhs, count);
487 }
488 
489 #define CREATE_SYSisspace(TYPE) \
490 inline bool \
491 SYSisspace(TYPE c) \
492 { \
493  /* Fastest exit for non-spaces. */ \
494  if (c > ' ') \
495  return false; \
496  /* Either equal to space, or between tab and carriage return */ \
497  return (c == ' ' || (c <= '\xd' && c >= '\x9')); \
498 }
499 
501 CREATE_SYSisspace(unsigned char)
502 CREATE_SYSisspace(signed char)
503 
504 #undef CREATE_SYSisspace
505 
506 // This function tries to smooth over the differences in thread-safe strerror
507 // between platforms. Returns true if the function succeeded, and false
508 // otherwise. buf must not be null, and must have size greater than zero.
509 static bool
510 SYSstrerror_r(int errnum, char *buf, size_t buf_size)
511 {
512 #ifdef WIN32
513  return ::strerror_s(buf, buf_size, errnum) == 0;
514 #elif defined(LINUX)
515 #if (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE
516  // Use the POSIX version of strerror_r
517  return ::strerror_r(errnum, buf, buf_size) == 0;
518 #else
519  // The GNU-specific strerror_r returns char* instead of int and may return a
520  // pointer to a statically-allocated string rather than part of the buffer.
521  char *str = ::strerror_r(errnum, buf, buf_size);
522  if (str == nullptr)
523  return false;
524  if (str != buf)
525  {
526  size_t len = strlen(str);
527  if (len >= buf_size)
528  len = buf_size - 1;
529  memmove(buf, str, len);
530  buf[len+1] = '\0';
531  }
532  return true;
533 #endif // (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE
534 #else // MacOS
535  return ::strerror_r(errnum, buf, buf_size) == 0;
536 #endif
537 }
538 
539 #endif
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glcorearb.h:2540
size_t SYSstrlcat(char *dest, const char *src, size_t size)
Definition: SYS_String.h:250
#define SYS_STATIC_ASSERT_MSG(expr, msg)
const GLdouble * v
Definition: glcorearb.h:837
#define SYS_IS_WRAPPER(TEST)
Definition: SYS_String.h:423
GLsizei const GLchar *const * string
Definition: glcorearb.h:814
#define CREATE_SYSisspace(TYPE)
Definition: SYS_String.h:489
int64 exint
Definition: SYS_Types.h:125
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
GLdouble s
Definition: glad.h:3009
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
SYS_FORCE_INLINE bool SYSisprint(unsigned char c)
Definition: SYS_String.h:413
#define SYS_EXINT_MAX
Definition: SYS_Types.h:181
unsigned long long uint64
Definition: SYS_Types.h:117
GLdouble n
Definition: glcorearb.h:2008
int SYSstrncasecmp(const char *a, const char *b, size_t n)
Definition: SYS_String.h:318
GLuint GLuint end
Definition: glcorearb.h:475
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
char * SYSstrtok(char *string, const char *delimit, char **context)
Definition: SYS_String.h:151
int SYSstrcmp(const char *a, const char *b)
Definition: SYS_String.h:282
size_t SYSstrlcpy(char *dest, const char *src, size_t size)
Definition: SYS_String.h:225
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
GLsizeiptr size
Definition: glcorearb.h:664
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:2002
char * SYSstrcasestr(const char *haystack, const char *needle)
Replacement for strcasestr, since no equivalent exists on Win32.
Definition: SYS_String.h:335
int SYSstrcasecmp(const char *a, const char *b)
Definition: SYS_String.h:265
unsigned int uint32
Definition: SYS_Types.h:40
char * SYSstrndup(const char *s, size_t n)
Definition: SYS_String.h:377
GLboolean r
Definition: glcorearb.h:1222
bool isalpha(const std::string &str)
Verify that str consists of letters only.
Definition: CLI11.h:340
GLint GLsizei count
Definition: glcorearb.h:405
#define WRAP_NULLTEST(FUNCTION)
Definition: SYS_String.h:303
GLenum src
Definition: glcorearb.h:1793