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