OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
Standard.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* All kinds of valuable helpers */
19 
20 #include "C4Include.h"
21 
22 //------------------------------------- Basics ----------------------------------------
23 
24 int32_t Distance(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2)
25 {
26  int64_t dx = int64_t(iX1)-iX2, dy = int64_t(iY1)-iY2;
27  int64_t d2 = dx*dx+dy*dy;
28  if (d2 < 0) return -1;
29  int32_t dist = int32_t(sqrt(double(d2)));
30  if (int64_t(dist)*dist < d2) ++dist;
31  if (int64_t(dist)*dist > d2) --dist;
32  return dist;
33 }
34 
35 int Angle(int iX1, int iY1, int iX2, int iY2)
36 {
37  int iAngle = (int) ( 180.0 * atan2( float(Abs(iY1-iY2)), float(Abs(iX1-iX2)) ) / M_PI );
38  if (iX2>iX1 )
39  { if (iY2<iY1) iAngle = 90-iAngle; else iAngle = 90+iAngle; }
40  else
41  { if (iY2<iY1) iAngle = 270+iAngle; else iAngle = 270-iAngle; }
42  return iAngle;
43 }
44 
45 /* Fast integer exponentiation */
46 int Pow(int base, int exponent)
47 {
48  if (exponent < 0) return 0;
49 
50  int result = 1;
51 
52  if (exponent & 1) result = base;
53  exponent >>= 1;
54 
55  while (exponent)
56  {
57  base *= base;
58  if (exponent & 1) result *= base;
59  exponent >>= 1;
60  }
61 
62  return result;
63 }
64 
65 //--------------------------------- Characters ------------------------------------------
66 
67 bool IsIdentifier(char cChar)
68 {
69  if (Inside(cChar,'A','Z')) return true;
70  if (Inside(cChar,'a','z')) return true;
71  if (Inside(cChar,'0','9')) return true;
72  if (cChar=='_') return true;
73  if (cChar=='~') return true;
74  if (cChar=='+') return true;
75  if (cChar=='-') return true;
76  return false;
77 }
78 
79 static bool IsNumber(char c, int base)
80 {
81  return (c >= '0' && c <= '9' && c < ('0' + base)) ||
82  (c >= 'a' && c <= 'z' && c < ('a' + base - 10)) ||
83  (c >= 'A' && c <= 'Z' && c < ('A' + base - 10));
84 }
85 
86 static int ToNumber(char c)
87 {
88  if (c >= '0' && c <= '9') return c - '0';
89  if (c >= 'a' && c <= 'z') return 10 + c - 'a';
90  if (c >= 'A' && c <= 'Z') return 10 + c - 'A';
91  assert(false);
92  return 0;
93 }
94 
95 //------------------------------- Strings ------------------------------------------------
96 
97 int32_t StrToI32(const char *s, int base, const char **scan_end)
98 {
99  int sign = 1;
100  int32_t result = 0;
101  if (*s == '-')
102  {
103  sign = -1;
104  s++;
105  }
106  else if (*s == '+')
107  {
108  s++;
109  }
110  while (IsNumber(*s,base))
111  {
112  int value = ToNumber(*s++);
113  assert (value < base && value >= 0);
114  result *= base;
115  result += value;
116  }
117  if (scan_end != 0L) *scan_end = s;
118  result *= sign;
119  return result;
120 }
121 
122 void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
123 {
124  if (szSource == sTarget) return;
125  if (!sTarget) return; *sTarget=0; if (!szSource) return;
126  while (*szSource && (iMaxL>0))
127  { *sTarget=*szSource; iMaxL--; szSource++; sTarget++; }
128  *sTarget=0;
129 }
130 
131 void SCopy(const char *szSource, char *sTarget)
132 {
133  if (szSource == sTarget) return;
134  if (!sTarget) return; *sTarget=0; if (!szSource) return;
135  strcpy(sTarget,szSource);
136 }
137 
138 void SCopyUntil(const char *szSource, char *sTarget, char cUntil, int iMaxL, int iIndex)
139 {
140  if (szSource == sTarget) return;
141  if (!sTarget) return; *sTarget=0; if (!szSource) return;
142  while ( *szSource && ((*szSource!=cUntil) || (iIndex>0)) && (iMaxL!=0) )
143  { *sTarget=*szSource; if (*szSource==cUntil) iIndex--; szSource++; sTarget++; iMaxL--; }
144  *sTarget=0;
145 }
146 
147 void SCopyUntil(const char *szSource, char *sTarget, const char * sUntil, size_t iMaxL)
148 {
149  size_t n = std::min(strcspn(szSource, sUntil), iMaxL - 1);
150  strncpy(sTarget, szSource, n);
151  sTarget[n] = 0;
152 }
153 
154 bool SEqualUntil(const char *szStr1, const char *szStr2, char cWild)
155 {
156  if (!szStr1 || !szStr2) return false;
157  while (*szStr1 || *szStr2)
158  {
159  if ((*szStr1==cWild) || (*szStr2==cWild)) return true;
160  if (*szStr1!=*szStr2) return false;
161  szStr1++; szStr2++;
162  }
163  return true;
164 }
165 
166 // Beginning of string 1 needs to match string 2.
167 
168 bool SEqual2(const char *szStr1, const char *szStr2)
169 {
170  if (!szStr1 || !szStr2) return false;
171  while (*szStr1 && *szStr2)
172  if (*szStr1++ != *szStr2++) return false;
173  if (*szStr2) return false; // Str1 is shorter
174  return true;
175 }
176 
177 bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
178 {
179  if (!szStr1 || !szStr2) return false;
180  if (iLen==0) return true;
181  while (*szStr1 && *szStr2)
182  {
183  if ( CharCapital(*szStr1++) != CharCapital(*szStr2++)) return false;
184  if (iLen>0) { iLen--; if (iLen==0) return true; }
185  }
186  if (*szStr1 || *szStr2) return false;
187  return true;
188 }
189 
190 bool SEqual2NoCase(const char *szStr1, const char *szStr2, int iLen)
191 {
192  if (!szStr1 || !szStr2) return false;
193  if (iLen==0) return true;
194  while (*szStr1 && *szStr2)
195  {
196  if ( CharCapital(*szStr1++) != CharCapital(*szStr2++)) return false;
197  if (iLen>0) { iLen--; if (iLen==0) return true; }
198  }
199  if (*szStr2) return false; // Str1 is shorter
200  return true;
201 }
202 
203 int SCharPos(char cTarget, const char *szInStr, int iIndex)
204 {
205  const char *cpos;
206  int ccpos;
207  if (!szInStr) return -1;
208  for (cpos=szInStr,ccpos=0; *cpos; cpos++,ccpos++)
209  if (*cpos==cTarget)
210  {
211  if (iIndex==0) return ccpos;
212  else iIndex--;
213  }
214  return -1;
215 }
216 
217 int SCharLastPos(char cTarget, const char *szInStr)
218 {
219  const char *cpos;
220  int ccpos,lcpos;
221  if (!szInStr) return -1;
222  for (cpos=szInStr,ccpos=0,lcpos=-1; *cpos; cpos++,ccpos++)
223  if (*cpos==cTarget) lcpos=ccpos;
224  return lcpos;
225 }
226 
227 void SAppend(const char *szSource, char *szTarget, int iMaxL)
228 {
229  if (iMaxL == -1)
230  SCopy(szSource, szTarget + SLen(szTarget));
231  else
232  SCopy(szSource, szTarget + SLen(szTarget), iMaxL - SLen(szTarget));
233 }
234 
235 void SAppendChar(char cChar, char *szStr)
236 {
237  if (!szStr) return;
238  char *cPos;
239  for (cPos=szStr; *cPos; cPos++) {}
240  *cPos=cChar; *(cPos+1)=0;
241 }
242 
243 bool SCopySegment(const char *szString, int iSegment, char *sTarget,
244  char cSeparator, int iMaxL, bool fSkipWhitespace)
245 {
246  // Advance to indexed segment
247  while (iSegment>0)
248  {
249  if (SCharPos(cSeparator,szString) == -1)
250  { sTarget[0]=0; return false; }
251  szString += SCharPos(cSeparator,szString)+1;
252  iSegment--;
253  }
254  // Advance whitespace
255  if (fSkipWhitespace)
256  szString = SAdvanceSpace(szString);
257  // Copy segment contents
258  SCopyUntil(szString,sTarget,cSeparator,iMaxL);
259  return true;
260 }
261 
262 bool SCopySegmentEx(const char *szString, int iSegment, char *sTarget,
263  char cSep1, char cSep2, int iMaxL, bool fSkipWhitespace)
264 {
265  // Advance to indexed segment
266  while (iSegment>0)
267  {
268  // use the separator that's closer
269  int iPos1 = SCharPos(cSep1,szString), iPos2 = SCharPos(cSep2,szString);
270  if (iPos1 == -1)
271  if (iPos2 == -1)
272  { sTarget[0]=0; return false; }
273  else
274  iPos1=iPos2;
275  else if (iPos2 != -1 && iPos2 < iPos1)
276  iPos1 = iPos2;
277  szString += iPos1+1;
278  iSegment--;
279  }
280  // Advance whitespace
281  if (fSkipWhitespace)
282  szString = SAdvanceSpace(szString);
283  // Copy segment contents; use separator that's closer
284  int iPos1 = SCharPos(cSep1,szString), iPos2 = SCharPos(cSep2,szString);
285  if (iPos2 != -1 && (iPos2 < iPos1 || iPos1 == -1)) cSep1 = cSep2;
286  SCopyUntil(szString,sTarget,cSep1,iMaxL);
287  return true;
288 }
289 
290 unsigned int SCharCount(char cTarget, const char *szInStr, const char *cpUntil)
291 {
292  unsigned int iResult=0;
293  // Scan string
294  while (*szInStr)
295  {
296  // End position reached (end character is not included)
297  if (szInStr==cpUntil) return iResult;
298  // Character found
299  if (*szInStr==cTarget) iResult++;
300  // Advance
301  szInStr++;
302  }
303  // Done
304  return iResult;
305 }
306 
307 unsigned int SCharCountEx(const char *szString, const char *szCharList)
308 {
309  unsigned int iResult = 0;
310  while ( *szCharList )
311  {
312  iResult += SCharCount( *szCharList, szString );
313  szCharList++;
314  }
315  return iResult;
316 }
317 
318 void SReplaceChar(char *str, char fc, char tc)
319 {
320  while (str && *str)
321  { if (*str==fc) *str=tc; str++; }
322 }
323 
324 void SCapitalize(char *str)
325 {
326  while (str && *str)
327  {
328  *str=CharCapital(*str);
329  str++;
330  }
331 }
332 
333 const char *SSearch(const char *szString, const char *szIndex)
334 {
335  const char *cscr;
336  size_t indexlen,match=0;
337  if (!szString || !szIndex) return nullptr;
338  indexlen=SLen(szIndex);
339  for (cscr=szString; cscr && *cscr; cscr++)
340  {
341  if (*cscr==szIndex[match]) match++;
342  else match=0;
343  if (match>=indexlen) return cscr+1;
344  }
345  return nullptr;
346 }
347 
348 const char *SSearchNoCase(const char *szString, const char *szIndex)
349 {
350  const char *cscr;
351  size_t indexlen,match=0;
352  if (!szString || !szIndex) return nullptr;
353  indexlen=SLen(szIndex);
354  for (cscr=szString; cscr && *cscr; cscr++)
355  {
356  if (CharCapital(*cscr)==CharCapital(szIndex[match])) match++;
357  else match=0;
358  if (match>=indexlen) return cscr+1;
359  }
360  return nullptr;
361 }
362 
363 void SWordWrap(char *szText, char cSpace, char cSepa, int iMaxLine)
364 {
365  if (!szText) return;
366  // Scan string
367  char *cPos,*cpLastSpace=nullptr;
368  int iLineRun=0;
369  for (cPos=szText; *cPos; cPos++)
370  {
371  // Store last space
372  if (*cPos==cSpace) cpLastSpace=cPos;
373  // Separator encountered: reset line run
374  if (*cPos==cSepa) iLineRun=0;
375  // Need a break, insert at last space
376  if (iLineRun>=iMaxLine)
377  if (cpLastSpace)
378  { *cpLastSpace=cSepa; iLineRun=cPos - cpLastSpace; }
379  // Line run
380  iLineRun++;
381  }
382 }
383 
384 const char *SAdvanceSpace(const char *szSPos)
385 {
386  if (!szSPos) return nullptr;
387  while (IsWhiteSpace(*szSPos)) szSPos++;
388  return szSPos;
389 }
390 
391 const char *SRewindSpace(const char *szSPos, const char *pBegin)
392 {
393  if (!szSPos || !pBegin) return nullptr;
394  while (IsWhiteSpace(*szSPos))
395  {
396  szSPos--;
397  if (szSPos<pBegin) return nullptr;
398  }
399  return szSPos;
400 }
401 
402 const char *SAdvancePast(const char *szSPos, char cPast)
403 {
404  if (!szSPos) return nullptr;
405  while (*szSPos)
406  {
407  if (*szSPos==cPast) { szSPos++; break; }
408  szSPos++;
409  }
410  return szSPos;
411 }
412 
413 void SCopyIdentifier(const char *szSource, char *sTarget, int iMaxL)
414 {
415  if (!szSource || !sTarget) return;
416  while (IsIdentifier(*szSource))
417  {
418  if (iMaxL==1) { *sTarget++ = *szSource++; break; }
419  iMaxL--;
420  *sTarget++ = *szSource++;
421  }
422  *sTarget=0;
423 }
424 
425 int SClearFrontBack(char *szString, char cClear)
426 {
427  int cleared=0;
428  char *cpos;
429  if (!szString) return 0;
430  for (cpos=szString; *cpos && (*cpos==cClear); cpos++,cleared++) {}
431  // strcpy is undefined when used on overlapping strings...
432  if (cpos!=szString) memmove(szString, cpos, SLen(cpos) + 1);
433  for (cpos=szString+SLen(szString)-1; (cpos>szString) && (*cpos==cClear); cpos--,cleared++)
434  *cpos=0x00;
435  return cleared;
436 }
437 
438 void SNewSegment(char *szStr, const char *szSepa)
439 {
440  if (szStr[0]) SAppend(szSepa,szStr);
441 }
442 
443 int SGetLine(const char *szText, const char *cpPosition)
444 {
445  if (!szText || !cpPosition) return 0;
446  int iLines = 1;
447  while (*szText && (szText<cpPosition))
448  {
449  if (*szText == 0x0A) iLines++;
450  szText++;
451  }
452  return iLines;
453 }
454 
455 int SLineGetCharacters(const char *szText, const char *cpPosition)
456 {
457  if (!szText || !cpPosition) return 0;
458  int iChars = 1;
459  while (*szText && (szText<cpPosition))
460  {
461  if (*szText == 0x0A)
462  iChars = 1;
463  else if (*szText == '\t')
464  // assume a tab stop every 8 characters
465  iChars = ((iChars - 1 + 8) & ~7) + 1;
466  else
467  iChars++;
468  szText++;
469  }
470  return iChars;
471 }
472 
473 void SInsert(char *szString, const char *szInsert, int iPosition, int iMaxLen)
474 {
475  // Safety
476  if (!szString || !szInsert || !szInsert[0]) return;
477  size_t insertlen = strlen(szInsert);
478  if (iMaxLen >= 0 && strlen(szString) + insertlen > (size_t) iMaxLen) return;
479  // Move up string remainder
480  memmove (szString + iPosition + insertlen, szString + iPosition, SLen(szString+ iPosition) + 1);
481  // Copy insertion
482  MemCopy( szInsert, szString+iPosition, SLen(szInsert) );
483 }
484 
485 void SDelete(char *szString, int iLen, int iPosition)
486 {
487  // Safety
488  if (!szString) return;
489  // Move down string remainder
490  MemCopy( szString+iPosition+iLen, szString+iPosition, SLen(szString+iPosition+iLen)+1 );
491 }
492 
493 bool SCopyEnclosed(const char *szSource, char cOpen, char cClose, char *sTarget, int iSize)
494 {
495  int iPos,iLen;
496  if (!szSource || !sTarget) return false;
497  if ((iPos = SCharPos(cOpen,szSource)) < 0) return false;
498  if ((iLen = SCharPos(cClose,szSource+iPos+1)) < 0) return false;
499  SCopy(szSource+iPos+1,sTarget,std::min(iLen,iSize));
500  return true;
501 }
502 
503 bool SGetModule(const char *szList, int iIndex, char *sTarget, int iSize)
504 {
505  if (!szList || !sTarget) return false;
506  if (!SCopySegment(szList,iIndex,sTarget,';',iSize)) return false;
507  SClearFrontBack(sTarget);
508  return true;
509 }
510 
511 bool SIsModule(const char *szList, const char *szString, int *ipIndex, bool fCaseSensitive)
512 {
513  char szModule[1024+1];
514  // Compare all modules
515  for (int iMod=0; SGetModule(szList,iMod,szModule,1024); iMod++)
516  if (fCaseSensitive ? SEqual(szString,szModule) : SEqualNoCase(szString,szModule))
517  {
518  // Provide index if desired
519  if (ipIndex) *ipIndex = iMod;
520  // Found
521  return true;
522  }
523  // Not found
524  return false;
525 }
526 
527 bool SAddModule(char *szList, const char *szModule, bool fCaseSensitive)
528 {
529  // Safety / no empties
530  if (!szList || !szModule || !szModule[0]) return false;
531  // Already a module?
532  if (SIsModule(szList,szModule,nullptr,fCaseSensitive)) return false;
533  // New segment, add string
534  SNewSegment(szList);
535  SAppend(szModule,szList);
536  // Success
537  return true;
538 }
539 
540 bool SAddModules(char *szList, const char *szModules, bool fCaseSensitive)
541 {
542  // Safety / no empties
543  if (!szList || !szModules || !szModules[0]) return false;
544  // Add modules
545  char szModule[1024+1]; // limited
546  for (int cnt=0; SGetModule(szModules,cnt,szModule,1024); cnt++)
547  SAddModule(szList,szModule,fCaseSensitive);
548  // Success
549  return true;
550 }
551 
552 bool SRemoveModule(char *szList, const char *szModule, bool fCaseSensitive)
553 {
554  int iMod,iPos,iLen;
555  // Not a module
556  if (!SIsModule(szList,szModule,&iMod,fCaseSensitive)) return false;
557  // Get module start
558  iPos = 0;
559  if (iMod > 0) iPos = SCharPos(';',szList,iMod-1)+1;
560  // Get module length
561  iLen = SCharPos(';',szList+iPos);
562  if (iLen<0) iLen=SLen(szList); else iLen+=1;
563  // Delete module
564  SDelete(szList,iLen,iPos);
565  // Success
566  return true;
567 }
568 
569 bool SRemoveModules(char *szList, const char *szModules, bool fCaseSensitive)
570 {
571  // Safety / no empties
572  if (!szList || !szModules || !szModules[0]) return false;
573  // Remove modules
574  char szModule[1024+1]; // limited
575  for (int cnt=0; SGetModule(szModules,cnt,szModule,1024); cnt++)
576  SRemoveModule(szList,szModule,fCaseSensitive);
577  // Success
578  return true;
579 }
580 
581 int SModuleCount(const char *szList)
582 {
583  if (!szList) return 0;
584  int iCount = 0;
585  bool fNewModule = true;
586  while (*szList)
587  {
588  switch (*szList)
589  {
590  case ' ': break;
591  case ';': fNewModule=true; break;
592  default: if (fNewModule) iCount++; fNewModule=false; break;
593  }
594  szList++;
595  }
596  return iCount;
597 }
598 
599 bool SWildcardMatchEx(const char *szString, const char *szWildcard)
600 {
601  // safety
602  if (!szString || !szWildcard) return false;
603  // match char-wise
604  const char *pWild = szWildcard, *pPos = szString;
605  const char *pLWild = nullptr, *pLPos = nullptr; // backtracking
606  while (*pWild || pLWild)
607  // string wildcard?
608  if (*pWild == '*')
609  { pLWild = ++pWild; pLPos = pPos; }
610  // nothing left to match?
611  else if (!*pPos)
612  break;
613  // equal or one-character-wildcard? proceed
614  else if (*pWild == '?' || *pWild == *pPos)
615  { pWild++; pPos++; }
616  // backtrack possible?
617  else if (pLPos)
618  { pWild = pLWild; pPos = ++pLPos; }
619  // match failed
620  else
621  return false;
622  // match complete if both strings are fully matched
623  return !*pWild && !*pPos;
624 }
625 
626 // UTF-8 conformance checking
627 namespace
628 {
629  static const int utf8_continuation_byte_table[256] =
630  {
631  // How many continuation bytes must follow a byte with this value?
632  // Negative values mean that this byte can never start a valid
633  // UTF-8 sequence.
634  // Note that while the encoding scheme allows more than three
635  // trailing bytes in principle, it is not actually allowed for UTF-8.
636  // Values 0xC0 and 0xC1 can never occur in UTF-8 because they
637  // would mark the beginning of an overlong encoding of characters
638  // below 0x80.
639  // Values 0xF5 to 0xFD are invalid because they can only be used
640  // to encode characters beyond the Unicode range.
641  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0b00000000..0b00001111, 0x00..0x0F
642  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0b00010000..0b00011111, 0x10..0x1F
643  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0b00100000..0b00101111, 0x20..0x2F
644  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0b00110000..0b00111111, 0x30..0x3F
645  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0b01000000..0b01001111, 0x40..0x4F
646  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0b01010000..0b01011111, 0x50..0x5F
647  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0b01100000..0b01101111, 0x60..0x6F
648  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0b01110000..0b01111111, 0x70..0x7F
649  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0b10000000..0b10001111, 0x80..0x8F
650  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0b10010000..0b10011111, 0x90..0x9F
651  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0b10100000..0b10101111, 0xA0..0xAF
652  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0b10110000..0b10111111, 0xB0..0xBF
653  -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0b11000000..0b11001111, 0xC0..0xCF
654  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0b11010000..0b11011111, 0xD0..0xDF
655  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0b11100000..0b11101111, 0xE0..0xEF
656  3, 3, 3, 3, 3, -3, -3, -3, -4, -4, -4, -4, -5, -5, -1, -1 // 0b11110000..0b11111111, 0xF0..0xFF
657  };
658  static const uint32_t utf8_min_char_value[4] =
659  {
660  // Which is the lowest character value that may be encoded
661  // using this many continuation bytes?
662  0, 0x80, 0x800, 0x10000
663  };
664 }
665 
666 bool IsValidUtf8(const char *text, int length)
667 {
668  // Intentionally using a C-style cast to always get a uint8_t* from char*;
669  // reinterpret_cast would fail here on platforms that have unsigned char,
670  // while static_cast would fail on platforms with a signed char type
671  const uint8_t *input = (const uint8_t*)(text);
672 
673  for (const uint8_t *cursor = input; length < 0 ? *cursor != 0 : cursor - input < length; ++cursor)
674  {
675  int continuation_bytes = utf8_continuation_byte_table[*cursor];
676  if (continuation_bytes < 0)
677  return false;
678  else if (continuation_bytes == 0)
679  {
680  // Standard 7-bit ASCII value (i.e., 1 byte codepoint)
681  continue;
682  }
683  else if (length >= 0 && cursor - input + continuation_bytes >= length)
684  {
685  // Too few remaining bytes
686  return false;
687  }
688 
689  // Compute character value, so we can detect overlong sequences
690  assert((*cursor & 0xC0) == 0xC0);
691  uint32_t value = *cursor;
692  // strip length bits off the start byte
693  value &= (0xFF >> (continuation_bytes + 1));
694  for (int byte = 0; byte < continuation_bytes; ++byte)
695  {
696  // check that this is actually a continuation byte
697  if ((cursor[byte + 1] & 0xC0) != 0x80)
698  return false;
699  // merge continuation byte into value
700  value <<= 6;
701  value |= cursor[byte + 1] & 0x3F;
702  }
703  // make sure this is not overlong
704  if (value < utf8_min_char_value[continuation_bytes])
705  return false;
706  // and also not beyond 0x10FFFF
707  if (value > 0x10FFFF)
708  return false;
709  // and also not a wrongly encoded UTF-16 surrogate half
710  if (value >= 0xD800 && value <= 0xDFFF)
711  return false;
712  cursor += continuation_bytes;
713  }
714  // Looks fine
715  return true;
716 }
717 
718 // UTF-8 iteration
719 uint32_t GetNextUTF8Character(const char **pszString)
720 {
721  // assume the current character is UTF8 already (i.e., highest bit set)
722  const uint32_t REPLACEMENT_CHARACTER = 0xFFFDu;
723  const char *szString = *pszString;
724  unsigned char c = *szString++;
725  uint32_t dwResult = REPLACEMENT_CHARACTER;
726  assert(c>127);
727  if (c>191 && c<224)
728  {
729  unsigned char c2 = *szString++;
730  if ((c2 & 192) != 128) { *pszString = szString; return REPLACEMENT_CHARACTER; }
731  dwResult = (int(c&31)<<6) | (c2&63); // two char code
732  }
733  else if (c >= 224 && c <= 239)
734  {
735  unsigned char c2 = *szString++;
736  if ((c2 & 192) != 128) { *pszString = szString; return REPLACEMENT_CHARACTER; }
737  unsigned char c3 = *szString++;
738  if ((c3 & 192) != 128) { *pszString = szString; return REPLACEMENT_CHARACTER; }
739  dwResult = (int(c&15)<<12) | (int(c2&63)<<6) | int(c3&63); // three char code
740  }
741  else if (c >= 240 && c <= 247)
742  {
743  unsigned char c2 = *szString++;
744  if ((c2 & 192) != 128) { *pszString = szString; return REPLACEMENT_CHARACTER; }
745  unsigned char c3 = *szString++;
746  if ((c3 & 192) != 128) { *pszString = szString; return REPLACEMENT_CHARACTER; }
747  unsigned char c4 = *szString++;
748  if ((c4 & 192) != 128) { *pszString = szString; return REPLACEMENT_CHARACTER; }
749  dwResult = (int(c&7)<<18) | (int(c2&63)<<12) | (int(c3&63)<<6) | int(c4&63); // four char code
750  }
751  *pszString = szString;
752  return dwResult;
753 }
754 
755 int GetCharacterCount(const char * s)
756 {
757  int l = 0;
758  while (*s)
759  {
760  unsigned char c = *s;
761  if (c < 128 || c > 247)
762  {
763  ++l;
764  s += 1;
765  }
766  else if (c > 191 && c < 224)
767  {
768  ++l;
769  s += 2;
770  }
771  else if (c >= 224 && c <= 239)
772  {
773  ++l;
774  s += 3;
775  }
776  else if (c >= 240 && c <= 247)
777  {
778  ++l;
779  s += 4;
780  }
781  else assert(false);
782  }
783  return l;
784 }
785 
786 std::string vstrprintf(const char *format, va_list args)
787 {
788  va_list argcopy;
789  va_copy(argcopy, args);
790  int size = vsnprintf(nullptr, 0, format, argcopy);
791  if (size < 0)
792  throw std::invalid_argument("invalid argument to strprintf");
793  va_end(argcopy);
794  std::string s;
795  s.resize(size + 1);
796  size = vsnprintf(&s[0], s.size(), format, args);
797  assert(size >= 0);
798  s.resize(size);
799  return s;
800 }
801 
802 std::string strprintf(const char *format, ...)
803 {
804  va_list args;
805  va_start(args, format);
806  std::string s = vstrprintf(format, args);
807  va_end(args);
808  return s;
809 }
bool SGetModule(const char *szList, int iIndex, char *sTarget, int iSize)
Definition: Standard.cpp:503
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:122
void SAppendChar(char cChar, char *szStr)
Definition: Standard.cpp:235
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:227
int SGetLine(const char *szText, const char *cpPosition)
Definition: Standard.cpp:443
bool SCopySegment(const char *szString, int iSegment, char *sTarget, char cSeparator, int iMaxL, bool fSkipWhitespace)
Definition: Standard.cpp:243
const char * SSearch(const char *szString, const char *szIndex)
Definition: Standard.cpp:333
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:177
bool SCopyEnclosed(const char *szSource, char cOpen, char cClose, char *sTarget, int iSize)
Definition: Standard.cpp:493
const char * SAdvanceSpace(const char *szSPos)
Definition: Standard.cpp:384
bool IsWhiteSpace(char cChar)
Definition: Standard.h:76
int GetCharacterCount(const char *s)
Definition: Standard.cpp:755
void SCapitalize(char *str)
Definition: Standard.cpp:324
bool SIsModule(const char *szList, const char *szString, int *ipIndex, bool fCaseSensitive)
Definition: Standard.cpp:511
size_t SLen(const char *sptr)
Definition: Standard.h:78
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:97
unsigned int SCharCount(char cTarget, const char *szInStr, const char *cpUntil)
Definition: Standard.cpp:290
const char * SSearchNoCase(const char *szString, const char *szIndex)
Definition: Standard.cpp:348
unsigned int SCharCountEx(const char *szString, const char *szCharList)
Definition: Standard.cpp:307
int SLineGetCharacters(const char *szText, const char *cpPosition)
Definition: Standard.cpp:455
bool SAddModules(char *szList, const char *szModules, bool fCaseSensitive)
Definition: Standard.cpp:540
bool SRemoveModules(char *szList, const char *szModules, bool fCaseSensitive)
Definition: Standard.cpp:569
int32_t Distance(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2)
Definition: Standard.cpp:24
void SWordWrap(char *szText, char cSpace, char cSepa, int iMaxLine)
Definition: Standard.cpp:363
int iResult
Definition: C4GroupMain.cpp:39
int SCharLastPos(char cTarget, const char *szInStr)
Definition: Standard.cpp:217
void SInsert(char *szString, const char *szInsert, int iPosition, int iMaxLen)
Definition: Standard.cpp:473
bool SRemoveModule(char *szList, const char *szModule, bool fCaseSensitive)
Definition: Standard.cpp:552
bool SWildcardMatchEx(const char *szString, const char *szWildcard)
Definition: Standard.cpp:599
bool SCopySegmentEx(const char *szString, int iSegment, char *sTarget, char cSep1, char cSep2, int iMaxL, bool fSkipWhitespace)
Definition: Standard.cpp:262
int SModuleCount(const char *szList)
Definition: Standard.cpp:581
std::string vstrprintf(const char *format, va_list args)
Definition: Standard.cpp:786
int Angle(int iX1, int iY1, int iX2, int iY2)
Definition: Standard.cpp:35
bool IsValidUtf8(const char *text, int length)
Definition: Standard.cpp:666
void MemCopy(const void *lpMem1, void *lpMem2, size_t dwSize)
Definition: Standard.h:68
void SDelete(char *szString, int iLen, int iPosition)
Definition: Standard.cpp:485
int32_t StrToI32(const char *s, int base, const char **scan_end)
Definition: Standard.cpp:97
char CharCapital(char cChar)
Definition: Standard.h:74
void SNewSegment(char *szStr, const char *szSepa)
Definition: Standard.cpp:438
int SClearFrontBack(char *szString, char cClear)
Definition: Standard.cpp:425
bool SEqualUntil(const char *szStr1, const char *szStr2, char cWild)
Definition: Standard.cpp:154
bool IsIdentifier(char cChar)
Definition: Standard.cpp:67
bool SEqual2(const char *szStr1, const char *szStr2)
Definition: Standard.cpp:168
bool SEqual2NoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:190
int Pow(int base, int exponent)
Definition: Standard.cpp:46
int SCharPos(char cTarget, const char *szInStr, int iIndex)
Definition: Standard.cpp:203
T Abs(T val)
Definition: Standard.h:44
std::string strprintf(const char *format,...)
Definition: Standard.cpp:802
const char * SAdvancePast(const char *szSPos, char cPast)
Definition: Standard.cpp:402
const char * SRewindSpace(const char *szSPos, const char *pBegin)
Definition: Standard.cpp:391
#define s
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:45
bool SAddModule(char *szList, const char *szModule, bool fCaseSensitive)
Definition: Standard.cpp:527
void SCopyUntil(const char *szSource, char *sTarget, char cUntil, int iMaxL, int iIndex)
Definition: Standard.cpp:138
void SReplaceChar(char *str, char fc, char tc)
Definition: Standard.cpp:318
uint32_t GetNextUTF8Character(const char **pszString)
Definition: Standard.cpp:719
int iSize
Definition: TstC4NetIO.cpp:35
void SCopyIdentifier(const char *szSource, char *sTarget, int iMaxL)
Definition: Standard.cpp:413