root/src/libsir/include/sir/helpers.h

/* [previous][next][first][last][top][bottom][index][help] */

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. __sir_validptr
  2. __sir_validptrptr
  3. _sir_bittest
  4. _sir_setbitshigh
  5. _sir_setbitslow
  6. _sir_validfileid
  7. _sir_validpluginid
  8. _sir_defaultlevels
  9. _sir_defaultopts
  10. _sir_mkansifgcolor
  11. _sir_mkansibgcolor
  12. _sir_getansifgcmd
  13. _sir_getansibgcmd
  14. _sir_makergb
  15. __sir_validstr
  16. _sir_resetstr
  17. _sir_strsame
  18. _sir_localtime
  19. _sir_formattime
  20. FNV32_1a
  21. SANITIZE_SUPPRESS

   1 /*
   2  * helpers.h
   3  *
   4  * Version: 2.2.5
   5  *
   6  * -----------------------------------------------------------------------------
   7  *
   8  * SPDX-License-Identifier: MIT
   9  *
  10  * Copyright (c) 2018-2024 Ryan M. Lederman <lederman@gmail.com>
  11  * Copyright (c) 2018-2024 Jeffrey H. Johnson <trnsz@pobox.com>
  12  *
  13  * Permission is hereby granted, free of charge, to any person obtaining a copy of
  14  * this software and associated documentation files (the "Software"), to deal in
  15  * the Software without restriction, including without limitation the rights to
  16  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  17  * the Software, and to permit persons to whom the Software is furnished to do so,
  18  * subject to the following conditions:
  19  *
  20  * The above copyright notice and this permission notice shall be included in all
  21  * copies or substantial portions of the Software.
  22  *
  23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  25  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  26  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  27  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  28  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29  *
  30  * -----------------------------------------------------------------------------
  31  */
  32 
  33 #ifndef _SIR_HELPERS_H_INCLUDED
  34 # define _SIR_HELPERS_H_INCLUDED
  35 
  36 # include "sir/types.h"
  37 # include "sir/errors.h"
  38 
  39 # if defined(__cplusplus)
  40 extern "C" {
  41 # endif
  42 
  43 /** Computes the size of an array. */
  44 # define _sir_countof(arr) (sizeof(arr) / sizeof(arr[0]))
  45 
  46 /** Evil macro used for _sir_lv wrappers. */
  47 # define _SIR_L_START(format) \
  48     bool ret = false; \
  49     va_list args; \
  50     do { \
  51         if (!_sir_validptr(format)) \
  52             return false; \
  53         va_start(args, format); \
  54     } while (false)
  55 
  56 /** Evil macro used for _sir_lv wrappers. */
  57 # define _SIR_L_END() \
  58     va_end(args)
  59 
  60 /** Evil macros used to enter/leave locked sections. */
  61 # define _SIR_LOCK_SECTION(type, name, mid, ret) \
  62     type* name = _sir_locksection(mid); \
  63     do { \
  64         if (!name) { \
  65             (void)_sir_seterror(_SIR_E_INTERNAL); \
  66             return ret; \
  67         } \
  68     } while (false)
  69 
  70 /** Evil macros used to enter/leave locked sections. */
  71 # define _SIR_UNLOCK_SECTION(mid) \
  72     _sir_unlocksection(mid)
  73 
  74 /** Squelches warnings about unreferenced parameters. */
  75 # define SIR_UNUSED(param) (void)param
  76 
  77 /** Combines SIR_ASSERT and SIR_UNUSED, which are frequently used together. */
  78 # define SIR_ASSERT_UNUSED(assertion, var) SIR_ASSERT(assertion); SIR_UNUSED(var)
  79 
  80 /** Returns a printable string even if NULL. */
  81 # define _SIR_PRNSTR(str) (str ? str : "<null>")
  82 
  83 /** Even more evil macros used for binary searching arrays. */
  84 # define _SIR_DECLARE_BIN_SEARCH(low, high) \
  85     size_t _low = low, _high = high; \
  86     size_t _mid = (_low + _high) / 2
  87 
  88 # define _SIR_BEGIN_BIN_SEARCH() do {
  89 
  90 # define _SIR_ITERATE_BIN_SEARCH(comparison) \
  91     if (_low == _high) \
  92         break; \
  93     if (0 > comparison && (_mid - 1) >= _low) { \
  94         _high = _mid - 1; \
  95     } else if ((_mid + 1) <= _high) { \
  96         _low = _mid + 1; \
  97     } else { \
  98         break; \
  99     } \
 100     _mid = (_low + _high) / 2
 101 
 102 # define _SIR_END_BIN_SEARCH() \
 103     } while (true)
 104 
 105 /* Validates a pointer and optionally fails if it's invalid. */
 106 static inline
 107 bool __sir_validptr(const void* restrict p, bool fail, const char* func,
     /* [previous][next][first][last][top][bottom][index][help] */
 108     const char* file, uint32_t line) {
 109     bool valid = NULL != p;
 110     if (fail && !valid) {
 111         (void)__sir_seterror(_SIR_E_NULLPTR, func, file, line);
 112         SIR_ASSERT(!valid && fail);
 113     }
 114     return valid;
 115 }
 116 
 117 /** Validates a pointer-to-pointer and optionally fails if it's invalid. */
 118 static inline
 119 bool __sir_validptrptr(const void* restrict* pp, bool fail, const char* func,
     /* [previous][next][first][last][top][bottom][index][help] */
 120     const char* file, uint32_t line) {
 121     bool valid = NULL != pp;
 122     if (fail && !valid) {
 123         (void)__sir_seterror(_SIR_E_NULLPTR, func, file, line);
 124         SIR_ASSERT(!valid && fail);
 125     }
 126     return valid;
 127 }
 128 
 129 /** Validates a pointer but ignores whether it's invalid. */
 130 # define _sir_validptrnofail(p) \
 131     __sir_validptr(p, false, __func__, __file__, __LINE__)
 132 
 133 /** Validates a pointer and fails if it's invalid. */
 134 # define _sir_validptr(p) \
 135     __sir_validptr((const void* restrict)p, true, __func__, __file__, __LINE__)
 136 
 137 /** Validates a pointer-to-function and fails if it's invalid. */
 138 # define _sir_validfnptr(fnp) \
 139     __sir_validptrptr((const void* restrict*)&fnp, true, __func__, __file__, __LINE__)
 140 
 141 /** Validates a pointer-to-pointer and fails if it's invalid. */
 142 # define _sir_validptrptr(pp) \
 143     __sir_validptrptr((const void* restrict*)pp, true, __func__, __file__, __LINE__)
 144 
 145 /** Checks a bitmask for a specific set of bits. */
 146 static inline
 147 bool _sir_bittest(uint32_t flags, uint32_t test) {
     /* [previous][next][first][last][top][bottom][index][help] */
 148     return (flags & test) == test;
 149 }
 150 
 151 /** Sets a specific set of bits high in a bitmask. */
 152 static inline
 153 void _sir_setbitshigh(uint32_t* flags, uint32_t set) {
     /* [previous][next][first][last][top][bottom][index][help] */
 154     if (NULL != flags)
 155         *flags |= set;
 156 }
 157 
 158 /** Sets a specific set of bits low in a bitmask. */
 159 static inline
 160 void _sir_setbitslow(uint32_t* flags, uint32_t set) {
     /* [previous][next][first][last][top][bottom][index][help] */
 161     if (NULL != flags)
 162         *flags &= ~set;
 163 }
 164 
 165 /** Effectively performs b &= expr without the linter warnings about using
 166  * bool as an operand for that operator. */
 167 # define _sir_eqland(b, expr) ((b) = (expr) && (b))
 168 
 169 /** Calls free and sets the pointer to NULL. */
 170 void __sir_safefree(void** pp);
 171 
 172 /** Wraps __sir_safefree with a cast to void**. */
 173 # define _sir_safefree(pp) __sir_safefree((void**)pp)
 174 
 175 /** Calls close and sets the descriptor to -1. */
 176 void _sir_safeclose(int* restrict fd);
 177 
 178 /** Calls fclose and sets the stream pointer to NULL. */
 179 void _sir_safefclose(FILE* restrict* restrict f);
 180 
 181 /** Validates a log file descriptor. */
 182 bool _sir_validfd(int fd);
 183 
 184 /** Validates a file identifier */
 185 static inline
 186 bool _sir_validfileid(sirfileid id) {
     /* [previous][next][first][last][top][bottom][index][help] */
 187     return 0U != id;
 188 }
 189 
 190 /** Validates a plugin identifier */
 191 static inline
 192 bool _sir_validpluginid(sirpluginid id) {
     /* [previous][next][first][last][top][bottom][index][help] */
 193     return 0U != id;
 194 }
 195 
 196 /** Validates a sir_update_config_data structure. */
 197 bool __sir_validupdatedata(const sir_update_config_data* data, const char* func,
 198     const char* file, uint32_t line);
 199 
 200 /** Validates a sir_update_config_data structure. */
 201 # define _sir_validupdatedata(data) \
 202     __sir_validupdatedata(data, __func__, __file__, __LINE__)
 203 
 204 /** Validates a set of ::sir_level flags. */
 205 bool __sir_validlevels(sir_levels levels, const char* func, const char* file,
 206     uint32_t line);
 207 
 208 /** Validates a set of ::sir_level flags. */
 209 # define _sir_validlevels(levels) \
 210     __sir_validlevels(levels, __func__, __file__, __LINE__)
 211 
 212 /** Validates a single ::sir_level. */
 213 bool __sir_validlevel(sir_level level, const char* func, const char* file,
 214     uint32_t line);
 215 
 216 /** Validates a single ::sir_level. */
 217 # define _sir_validlevel(level) \
 218     __sir_validlevel(level, __func__, __file__, __LINE__)
 219 
 220 /** Applies default ::sir_level flags if applicable. */
 221 static inline
 222 void _sir_defaultlevels(sir_levels* levels, sir_levels def) {
     /* [previous][next][first][last][top][bottom][index][help] */
 223     if (levels && SIRL_DEFAULT == *levels)
 224         *levels = def;
 225 }
 226 
 227 /** Applies default ::sir_options flags if applicable. */
 228 static inline
 229 void _sir_defaultopts(sir_options* opts, sir_options def) {
     /* [previous][next][first][last][top][bottom][index][help] */
 230     if (opts && SIRO_DEFAULT == *opts)
 231         *opts = def;
 232 }
 233 
 234 /** Validates a set of ::sir_option flags. */
 235 bool __sir_validopts(sir_options opts, const char* func, const char* file,
 236     uint32_t line);
 237 
 238 /** Validates a set of ::sir_option flags. */
 239 # define _sir_validopts(opts) \
 240     __sir_validopts(opts, __func__, __file__, __LINE__)
 241 
 242 /** Validates a ::sir_textattr. */
 243 bool __sir_validtextattr(sir_textattr attr, const char* func, const char* file,
 244     uint32_t line);
 245 
 246 /** Validates a ::sir_textattr. */
 247 # define _sir_validtextattr(attr) \
 248     __sir_validtextattr(attr, __func__, __file__, __LINE__)
 249 
 250 /** Validates a ::sir_textcolor based on color mode. */
 251 bool __sir_validtextcolor(sir_colormode mode, sir_textcolor color, const char* func,
 252     const char* file, uint32_t line);
 253 
 254 /** Validates a ::sir_textcolor based on color mode. */
 255 # define _sir_validtextcolor(mode, color) \
 256     __sir_validtextcolor(mode, color, __func__, __file__, __LINE__)
 257 
 258 /** Validates a ::sir_colormode. */
 259 bool __sir_validcolormode(sir_colormode mode, const char* func, const char* file,
 260     uint32_t line);
 261 
 262 /** Validates a ::sir_colormode. */
 263 # define _sir_validcolormode(mode) \
 264     __sir_validcolormode(mode, __func__, __file__, __LINE__)
 265 
 266 /** Converts a SIRTC_* value to a 16-color mode ANSI foreground color. */
 267 static inline
 268 sir_textcolor _sir_mkansifgcolor(sir_textcolor fg) {
     /* [previous][next][first][last][top][bottom][index][help] */
 269     return SIRTC_DEFAULT == fg ? 39 : fg < 8 ? fg + 30 : fg + 82;
 270 }
 271 
 272 /** Converts a SIRTC_* value to a 16-color mode ANSI background color. */
 273 static inline
 274 sir_textcolor _sir_mkansibgcolor(sir_textcolor bg) {
     /* [previous][next][first][last][top][bottom][index][help] */
 275     return SIRTC_DEFAULT == bg ? 49 : bg < 8 ? bg + 40 : bg + 92;
 276 }
 277 
 278 /** Returns the appropriate ANSI command for the specified foreground color. */
 279 static inline
 280 sir_textcolor _sir_getansifgcmd(sir_textcolor fg) {
     /* [previous][next][first][last][top][bottom][index][help] */
 281     return SIRTC_DEFAULT == fg ? 39 : 38;
 282 }
 283 
 284 /** Returns the appropriate ANSI command for the specified background color. */
 285 static inline
 286 sir_textcolor _sir_getansibgcmd(sir_textcolor bg) {
     /* [previous][next][first][last][top][bottom][index][help] */
 287     return SIRTC_DEFAULT == bg ? 49 : 48;
 288 }
 289 
 290 /** Extracts the red component out of an RGB color mode ::sir_textcolor. */
 291 # define _sir_getredfromcolor(color) (uint8_t)(((color) >> 16) & 0x000000ffU)
 292 
 293 /** Sets the red component in an RGB color mode ::sir_textcolor. */
 294 # define _sir_setredincolor(color, red) (color |= (((red) << 16) & 0x00ff0000U))
 295 
 296 /** Extracts the green component out of an RGB color mode ::sir_textcolor. */
 297 # define _sir_getgreenfromcolor(color) (uint8_t)(((color) >> 8) & 0x000000ffU)
 298 
 299 /** Sets the green component in an RGB color mode ::sir_textcolor. */
 300 # define _sir_setgreenincolor(color, green) ((color) |= (((green) << 8) & 0x0000ff00U))
 301 
 302 /** Extracts the blue component out of an RGB color mode ::sir_textcolor. */
 303 # define _sir_getbluefromcolor(color) (uint8_t)((color) & 0x000000ffU)
 304 
 305 /** Sets the blue component in an RGB color mode ::sir_textcolor. */
 306 # define _sir_setblueincolor(color, blue) ((color) |= ((blue) & 0x000000ffU))
 307 
 308 /** Sets the red, blue, and green components in an RGB color mode ::sir_textcolor. */
 309 static inline
 310 sir_textcolor _sir_makergb(sir_textcolor r, sir_textcolor g, sir_textcolor b) {
     /* [previous][next][first][last][top][bottom][index][help] */
 311     sir_textcolor retval = 0;
 312     _sir_setredincolor(retval, r);
 313     _sir_setgreenincolor(retval, g);
 314     _sir_setblueincolor(retval, b);
 315     return retval;
 316 }
 317 
 318 /** Validates a string pointer and optionally fails if it's invalid. */
 319 static inline
 320 bool __sir_validstr(const char* restrict str, bool fail, const char* func,
     /* [previous][next][first][last][top][bottom][index][help] */
 321     const char* file, uint32_t line) {
 322     bool valid = str && *str != '\0';
 323     if (fail && !valid) {
 324         SIR_ASSERT(!valid && fail);
 325         (void)__sir_seterror(_SIR_E_STRING, func, file, line);
 326     }
 327     return valid;
 328 }
 329 
 330 /** Validates a string pointer and fails if it's invalid. */
 331 # define _sir_validstr(str) \
 332     __sir_validstr(str, true, __func__, __file__, __LINE__)
 333 
 334 /** Validates a string pointer but ignores whether it's invalid. */
 335 # define _sir_validstrnofail(str) \
 336     __sir_validstr(str, false, __func__, __file__, __LINE__)
 337 
 338 /** Places a null terminator at the first index in a string buffer. */
 339 static inline
 340 void _sir_resetstr(char* str) {
     /* [previous][next][first][last][top][bottom][index][help] */
 341     if (NULL != str)
 342         *str = '\0';
 343 }
 344 
 345 /**
 346  * Wrapper for strncmp. Just easier to read and use if you only wish to
 347  * test for equality. Not case-sensitive.
 348  */
 349 static inline
 350 bool _sir_strsame(const char* lhs, const char* rhs, size_t count) {
     /* [previous][next][first][last][top][bottom][index][help] */
 351     return 0 == strncmp(lhs, rhs, count);
 352 }
 353 
 354 /**
 355  * Wrapper for str[n,l]cpy/strncpy_s. Determines which one to use
 356  * based on preprocessor macros.
 357  */
 358 int _sir_strncpy(char* restrict dest, size_t destsz,
 359     const char* restrict src, size_t count);
 360 
 361 /**
 362  * Wrapper for str[n,l]cat/strncat_s. Determines which one to use
 363  * based on preprocessor macros.
 364  */
 365 int _sir_strncat(char* restrict dest, size_t destsz,
 366     const char* restrict src, size_t count);
 367 
 368 /**
 369  * Wrapper for fopen/fopen_s. Determines which one to use
 370  * based on preprocessor macros.
 371  */
 372 int _sir_fopen(FILE* restrict* restrict streamptr, const char* restrict filename,
 373     const char* restrict mode);
 374 
 375 /**
 376  * Wrapper for localtime[_s,_r]. Determines which one to use
 377  * based on preprocessor macros.
 378  */
 379 static inline
 380 struct tm* _sir_localtime(const time_t* timer, struct tm* buf) {
     /* [previous][next][first][last][top][bottom][index][help] */
 381     if (!timer || !buf)
 382         return NULL;
 383 # if defined(__HAVE_STDC_SECURE_OR_EXT1__) && !defined(__EMBARCADEROC__)
 384 #  if !defined(__WIN__)
 385     struct tm* ret = localtime_s(timer, buf);
 386     if (!ret) {
 387         (void)_sir_handleerr(errno);
 388         return NULL;
 389     }
 390 #  else /* __WIN__ */
 391     errno_t ret = localtime_s(buf, timer);
 392     if (0 != ret) {
 393         (void)_sir_handleerr(ret);
 394         return NULL;
 395     }
 396 #  endif
 397     return buf;
 398 # else /* !__HAVE_STDC_SECURE_OR_EXT1__ */
 399 #  if !defined(__WIN__) || \
 400      (defined(__EMBARCADEROC__) && (__clang_major__ < 15))
 401     struct tm* ret = localtime_r(timer, buf);
 402 #  else
 403     struct tm* ret = localtime(timer);
 404 #  endif
 405     if (!ret)
 406         (void)_sir_handleerr(errno);
 407     return ret;
 408 # endif
 409 }
 410 
 411 /** Formats the current time as a string. */
 412 # if defined(__GNUC__)
 413 __attribute__ ((format (strftime, 3, 0)))
 414 # endif
 415 static inline
 416 bool _sir_formattime(time_t now, char* buffer, const char* format) {
     /* [previous][next][first][last][top][bottom][index][help] */
 417     if (!buffer || !format)
 418         return false;
 419 # if defined(__GNUC__) && !defined(__clang__) && \
 420     !(defined(__OPEN64__) || defined(__OPENCC__) || defined(__PCC__))
 421 #  pragma GCC diagnostic push
 422 #  pragma GCC diagnostic ignored "-Wformat-nonliteral"
 423 # endif
 424     struct tm timebuf;
 425     const struct tm* ptb = _sir_localtime(&now, &timebuf);
 426     return NULL != ptb && 0 != strftime(buffer, SIR_MAXTIME, format, ptb);
 427 # if defined(__GNUC__) && !defined(__clang__) && \
 428     !(defined(__OPEN64__) || defined(__OPENCC__) || defined(__PCC__))
 429 #  pragma GCC diagnostic pop
 430 # endif
 431 }
 432 
 433 /**
 434  * A portable "press any key to continue" implementation; On Windows, uses _getch().
 435  * otherwise, uses tcgetattr()/tcsetattr() and getchar().
 436  */
 437 bool _sir_getchar(char* input);
 438 
 439 /**
 440  * Wrapper for snprintf when truncation is intended.
 441  */
 442 # define _sir_snprintf_trunc(dst, size, ...) \
 443     do { \
 444       volatile size_t _n = size; \
 445       if (!snprintf(dst, _n, __VA_ARGS__)) { (void)_n; }; \
 446     } while (false)
 447 
 448 /**
 449  * Implementation of the 32-bit FNV-1a OWHF (http://isthe.com/chongo/tech/comp/fnv/)
 450  */
 451 static inline
 452 uint32_t FNV32_1a(const uint8_t* data, size_t len) {
     /* [previous][next][first][last][top][bottom][index][help] */
 453     uint32_t hash = 2166136261U;
 454     for (size_t n = 0; n < len; n++) {
 455         hash ^= (uint32_t)data[n];
 456         hash = (uint32_t)(hash * 16777619ULL);
 457     }
 458     return hash;
 459 }
 460 
 461 /**
 462  * Implementation of the 64-bit FNV-1a OWHF (http://isthe.com/chongo/tech/comp/fnv/)
 463  * watered down to only handle null-terminated strings.
 464  */
 465 # if defined(__clang__) /* only Clang has unsigned-integer-overflow; GCC BZ#96829 */
 466 SANITIZE_SUPPRESS("unsigned-integer-overflow")
     /* [previous][next][first][last][top][bottom][index][help] */
 467 # endif
 468 static inline
 469 uint64_t FNV64_1a(const char* str) {
 470     uint64_t hash = 14695981039346656037ULL;
 471     for (const char* c = str; *c; c++) {
 472         hash ^= (uint64_t)(unsigned char)(*c);
 473         hash *= 1099511628211ULL; /* unsigned-integer-overflow */
 474     }
 475     return hash;
 476 }
 477 
 478 /**
 479  * Unconditionally and explicitly fill the first "len" bytes of
 480  * the memory area pointed to by "ptr" with the constant byte "c".
 481  */
 482 void* _sir_explicit_memset(void *ptr, int c, size_t len);
 483 
 484 /**
 485  * Remove all occurrences of substring "sub" in string "str".
 486  */
 487 char* _sir_strremove(char *str, const char *sub);
 488 
 489 /**
 490  * Replace all occurrences of repeating whitespace characters
 491  * (i.e. ` `, `\\f`, `\\n`, `\\r`, `\\t`) in string "str" with
 492  * a single space character.
 493  */
 494 char* _sir_strsqueeze(char *str);
 495 
 496 /**
 497  * Redact all occurrences of substring "sub" in string "str" with character 'c'.
 498  */
 499 char* _sir_strredact(char *str, const char *sub, const char c);
 500 
 501 /**
 502  * Replace all occurrences of character 'c' in string "str" with character 'n'.
 503  */
 504 char* _sir_strreplace(char *str, const char c, const char n);
 505 
 506 /**
 507  * Replace up to 'max' occurrences of character 'c' in string "str"
 508  * with character 'n', returning the number of replacements performed.
 509  */
 510 size_t _sir_strcreplace(char *str, const char c, const char n, int32_t max);
 511 
 512 # if defined(__cplusplus)
 513 }
 514 # endif
 515 
 516 #endif /* !_SIR_HELPERS_H_INCLUDED */

/* [previous][next][first][last][top][bottom][index][help] */