LCOV - code coverage report
Current view: top level - src - pl_string.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 197 215 91.6 %
Date: 2025-03-29 09:04:10 Functions: 29 30 96.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 105 136 77.2 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of libplacebo.
       3                 :            :  *
       4                 :            :  * libplacebo is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Lesser General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2.1 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * libplacebo is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  * GNU Lesser General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU Lesser General Public
      15                 :            :  * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
      16                 :            :  */
      17                 :            : 
      18                 :            : #include "common.h"
      19                 :            : #include "hash.h"
      20                 :            : 
      21                 :     735040 : static void grow_str(void *alloc, pl_str *str, size_t len)
      22                 :            : {
      23                 :            :     // Like pl_grow, but with some extra headroom
      24         [ +  + ]:     735040 :     if (len > pl_get_size(str->buf))
      25                 :       1472 :         str->buf = pl_realloc(alloc, str->buf, len * 1.5);
      26                 :     735040 : }
      27                 :            : 
      28                 :     106028 : void pl_str_append(void *alloc, pl_str *str, pl_str append)
      29                 :            : {
      30                 :            :     // Also append an extra \0 for convenience, since a lot of the time
      31                 :            :     // this function will be used to generate a string buffer
      32                 :     106028 :     grow_str(alloc, str, str->len + append.len + 1);
      33         [ +  + ]:     106028 :     if (append.len) {
      34                 :     101171 :         memcpy(str->buf + str->len, append.buf, append.len);
      35                 :     101171 :         str->len += append.len;
      36                 :            :     }
      37                 :     106028 :     str->buf[str->len] = '\0';
      38                 :     106028 : }
      39                 :            : 
      40                 :     565554 : void pl_str_append_raw(void *alloc, pl_str *str, const void *ptr, size_t size)
      41                 :            : {
      42         [ +  + ]:     565554 :     if (!size)
      43                 :            :         return;
      44                 :     553285 :     grow_str(alloc, str, str->len + size);
      45                 :     553285 :     memcpy(str->buf + str->len, ptr, size);
      46                 :     553285 :     str->len += size;
      47                 :            : }
      48                 :            : 
      49                 :       3300 : void pl_str_append_asprintf(void *alloc, pl_str *str, const char *fmt, ...)
      50                 :            : {
      51                 :            :     va_list ap;
      52                 :       3300 :     va_start(ap, fmt);
      53                 :       3300 :     pl_str_append_vasprintf(alloc, str, fmt, ap);
      54                 :       3300 :     va_end(ap);
      55                 :       3300 : }
      56                 :            : 
      57                 :      74827 : void pl_str_append_vasprintf(void *alloc, pl_str *str, const char *fmt, va_list ap)
      58                 :            : {
      59                 :            :     // First, we need to determine the size that will be required for
      60                 :            :     // printing the entire string. Do this by making a copy of the va_list
      61                 :            :     // and printing it to a null buffer.
      62                 :            :     va_list copy;
      63         [ -  + ]:      74827 :     va_copy(copy, ap);
      64                 :            :     int size = vsnprintf(NULL, 0, fmt, copy);
      65                 :      74827 :     va_end(copy);
      66         [ -  + ]:      74827 :     if (size < 0)
      67                 :          0 :         return;
      68                 :            : 
      69                 :            :     // Make room in `str` and format to there directly
      70                 :      74827 :     grow_str(alloc, str, str->len + size + 1);
      71                 :      74827 :     str->len += vsnprintf((char *) (str->buf + str->len), size + 1, fmt, ap);
      72                 :            : }
      73                 :            : 
      74         [ +  - ]:          4 : int pl_str_sscanf(pl_str str, const char *fmt, ...)
      75                 :            : {
      76                 :            :     char *tmp = pl_strdup0(NULL, str);
      77                 :            :     va_list va;
      78                 :          4 :     va_start(va, fmt);
      79                 :          4 :     int ret = vsscanf(tmp, fmt, va);
      80                 :          4 :     va_end(va);
      81                 :          4 :     pl_free(tmp);
      82                 :          4 :     return ret;
      83                 :            : }
      84                 :            : 
      85                 :          2 : int pl_strchr(pl_str str, int c)
      86                 :            : {
      87         [ +  + ]:          2 :     if (!str.len)
      88                 :            :         return -1;
      89                 :            : 
      90                 :        595 :     void *pos = memchr(str.buf, c, str.len);
      91   [ +  +  +  - ]:        595 :     if (pos)
      92                 :          1 :         return (intptr_t) pos - (intptr_t) str.buf;
      93                 :            :     return -1;
      94                 :            : }
      95                 :            : 
      96                 :        245 : size_t pl_strspn(pl_str str, const char *accept)
      97                 :            : {
      98         [ +  + ]:        973 :     for (size_t i = 0; i < str.len; i++) {
      99         [ +  + ]:        961 :         if (!strchr(accept, str.buf[i]))
     100                 :        233 :             return i;
     101                 :            :     }
     102                 :            : 
     103                 :            :     return str.len;
     104                 :            : }
     105                 :            : 
     106                 :          2 : size_t pl_strcspn(pl_str str, const char *reject)
     107                 :            : {
     108   [ +  +  +  + ]:        645 :     for (size_t i = 0; i < str.len; i++) {
     109   [ +  +  +  + ]:        625 :         if (strchr(reject, str.buf[i]))
     110                 :          1 :             return i;
     111                 :            :     }
     112                 :            : 
     113                 :            :     return str.len;
     114                 :            : }
     115                 :            : 
     116                 :            : static inline bool pl_isspace(char c)
     117                 :            : {
     118                 :      19158 :     switch (c) {
     119                 :            :     case ' ':
     120                 :            :     case '\n':
     121                 :            :     case '\r':
     122                 :            :     case '\t':
     123                 :            :     case '\v':
     124                 :            :     case '\f':
     125                 :            :         return true;
     126                 :            :     default:
     127                 :            :         return false;
     128                 :            :     }
     129                 :            : }
     130                 :            : 
     131                 :        939 : pl_str pl_str_strip(pl_str str)
     132                 :            : {
     133   [ +  +  +  + ]:       3663 :     while (str.len && pl_isspace(str.buf[0])) {
     134                 :       2724 :         str.buf++;
     135                 :       2724 :         str.len--;
     136                 :            :     }
     137   [ +  +  +  + ]:      15334 :     while (str.len && pl_isspace(str.buf[str.len - 1]))
     138                 :            :         str.len--;
     139                 :        939 :     return str;
     140                 :            : }
     141                 :            : 
     142                 :         65 : int pl_str_find(pl_str haystack, pl_str needle)
     143                 :            : {
     144         [ +  + ]:         65 :     if (!needle.len)
     145                 :            :         return 0;
     146                 :            : 
     147         [ +  + ]:      11431 :     for (size_t i = 0; i + needle.len <= haystack.len; i++) {
     148         [ +  + ]:      11419 :         if (memcmp(&haystack.buf[i], needle.buf, needle.len) == 0)
     149                 :         51 :             return i;
     150                 :            :     }
     151                 :            : 
     152                 :            :     return -1;
     153                 :            : }
     154                 :            : 
     155                 :        598 : pl_str pl_str_split_char(pl_str str, char sep, pl_str *out_rest)
     156                 :            : {
     157         [ +  + ]:        598 :     int pos = pl_strchr(str, sep);
     158         [ -  + ]:        516 :     if (pos < 0) {
     159         [ +  - ]:         82 :         if (out_rest)
     160                 :         82 :             *out_rest = (pl_str) {0};
     161                 :         82 :         return str;
     162                 :            :     } else {
     163         [ +  + ]:        516 :         if (out_rest)
     164         [ +  + ]:       1025 :             *out_rest = pl_str_drop(str, pos + 1);
     165                 :        516 :         return pl_str_take(str, pos);
     166                 :            :     }
     167                 :            : }
     168                 :            : 
     169                 :         36 : pl_str pl_str_split_chars(pl_str str, const char *seps, pl_str *out_rest)
     170                 :            : {
     171                 :         36 :     int pos = pl_strcspn(str, seps);
     172         [ -  + ]:         36 :     if (pos < 0) {
     173         [ #  # ]:          0 :         if (out_rest)
     174                 :          0 :             *out_rest = (pl_str) {0};
     175                 :          0 :         return str;
     176                 :            :     } else {
     177         [ +  - ]:         36 :         if (out_rest)
     178         [ +  + ]:         53 :             *out_rest = pl_str_drop(str, pos + 1);
     179                 :         36 :         return pl_str_take(str, pos);
     180                 :            :     }
     181                 :            : }
     182                 :            : 
     183                 :         51 : pl_str pl_str_split_str(pl_str str, pl_str sep, pl_str *out_rest)
     184                 :            : {
     185                 :         51 :     int pos = pl_str_find(str, sep);
     186         [ +  + ]:         51 :     if (pos < 0) {
     187         [ +  - ]:         11 :         if (out_rest)
     188                 :         11 :             *out_rest = (pl_str) {0};
     189                 :         11 :         return str;
     190                 :            :     } else {
     191         [ +  - ]:         40 :         if (out_rest)
     192         [ +  + ]:         79 :             *out_rest = pl_str_drop(str, pos + sep.len);
     193                 :         40 :         return pl_str_take(str, pos);
     194                 :            :     }
     195                 :            : }
     196                 :            : 
     197                 :        321 : static bool get_hexdigit(pl_str *str, int *digit)
     198                 :            : {
     199   [ +  -  -  + ]:        321 :     while (str->len && pl_isspace(str->buf[0])) {
     200                 :          0 :         str->buf++;
     201                 :          0 :         str->len--;
     202                 :            :     }
     203                 :            : 
     204         [ -  + ]:        321 :     if (!str->len) {
     205                 :          0 :         *digit = -1; // EOF
     206                 :          0 :         return true;
     207                 :            :     }
     208                 :            : 
     209                 :        321 :     char c = str->buf[0];
     210                 :        321 :     str->buf++;
     211                 :        321 :     str->len--;
     212                 :            : 
     213         [ +  + ]:        321 :     if (c >= '0' && c <= '9') {
     214                 :        140 :         *digit = c - '0';
     215         [ +  + ]:        181 :     } else if (c >= 'a' && c <= 'f') {
     216                 :        180 :         *digit = c - 'a' + 10;
     217         [ -  + ]:          1 :     } else if (c >= 'A' && c <= 'F') {
     218                 :          0 :         *digit = c - 'A' + 10;
     219                 :            :     } else {
     220                 :            :         return false; // invalid char
     221                 :            :     }
     222                 :            : 
     223                 :            :     return true;
     224                 :            : }
     225                 :            : 
     226                 :         12 : bool pl_str_decode_hex(void *alloc, pl_str hex, pl_str *out)
     227                 :            : {
     228         [ -  + ]:         12 :     if (!out)
     229                 :            :         return false;
     230                 :            : 
     231                 :         12 :     uint8_t *buf = pl_alloc(alloc, hex.len / 2);
     232                 :            :     int len = 0;
     233                 :            : 
     234         [ +  + ]:        172 :     while (hex.len) {
     235                 :            :         int a, b;
     236   [ +  +  -  + ]:        161 :         if (!get_hexdigit(&hex, &a) || !get_hexdigit(&hex, &b))
     237                 :          1 :             goto error; // invalid char
     238         [ +  - ]:        160 :         if (a < 0) // EOF
     239                 :            :             break;
     240         [ -  + ]:        160 :         if (b < 0) // only one digit
     241                 :          0 :             goto error;
     242                 :            : 
     243                 :        160 :         buf[len++] = (a << 4) | b;
     244                 :            :     }
     245                 :            : 
     246                 :         11 :     *out = (pl_str) { buf, len };
     247                 :         11 :     return true;
     248                 :            : 
     249                 :            : error:
     250                 :          1 :     pl_free(buf);
     251                 :          1 :     return false;
     252                 :            : }
     253                 :            : 
     254                 :            : struct pl_str_builder_t {
     255                 :            :     PL_ARRAY(pl_str_template) templates;
     256                 :            :     pl_str args;
     257                 :            :     pl_str output;
     258                 :            : };
     259                 :            : 
     260                 :        273 : pl_str_builder pl_str_builder_alloc(void *alloc)
     261                 :            : {
     262                 :        273 :     pl_str_builder b = pl_zalloc_ptr(alloc, b);
     263                 :        273 :     return b;
     264                 :            : }
     265                 :            : 
     266                 :          0 : void pl_str_builder_free(pl_str_builder *b)
     267                 :            : {
     268         [ #  # ]:          0 :     if (*b)
     269                 :          0 :         pl_free_ptr(b);
     270                 :          0 : }
     271                 :            : 
     272                 :      18845 : void pl_str_builder_reset(pl_str_builder b)
     273                 :            : {
     274                 :      18845 :     *b = (struct pl_str_builder_t) {
     275                 :      18845 :         .templates.elem = b->templates.elem,
     276                 :      18845 :         .args.buf       = b->args.buf,
     277                 :      18845 :         .output.buf     = b->output.buf,
     278                 :            :     };
     279                 :      18845 : }
     280                 :            : 
     281                 :       3776 : uint64_t pl_str_builder_hash(const pl_str_builder b)
     282                 :            : {
     283                 :       3776 :     size_t size = b->templates.num * sizeof(b->templates.elem[0]);
     284                 :       3776 :     uint64_t hash = pl_mem_hash(b->templates.elem, size);
     285                 :            :     pl_hash_merge(&hash, pl_str_hash(b->args));
     286                 :       3776 :     return hash;
     287                 :            : }
     288                 :            : 
     289                 :        900 : pl_str pl_str_builder_exec(pl_str_builder b)
     290                 :            : {
     291                 :        900 :     pl_str args = b->args;
     292                 :            : 
     293                 :        900 :     b->output.len = 0;
     294         [ +  + ]:      42272 :     for (int i = 0; i < b->templates.num; i++) {
     295                 :      41372 :         size_t consumed = b->templates.elem[i](b, &b->output, args.buf);
     296         [ -  + ]:      41372 :         pl_assert(consumed <= args.len);
     297                 :            :         args = pl_str_drop(args, consumed);
     298                 :            :     }
     299                 :            : 
     300                 :            :     // Terminate with an extra \0 byte for convenience
     301                 :        900 :     grow_str(b, &b->output, b->output.len + 1);
     302                 :        900 :     b->output.buf[b->output.len] = '\0';
     303                 :        900 :     return b->output;
     304                 :            : }
     305                 :            : 
     306                 :     103183 : void pl_str_builder_append(pl_str_builder b, pl_str_template tmpl,
     307                 :            :                            const void *args, size_t size)
     308                 :            : {
     309   [ +  +  +  +  :     103183 :     PL_ARRAY_APPEND(b, b->templates, tmpl);
                   -  + ]
     310                 :     103183 :     pl_str_append_raw(b, &b->args, args, size);
     311                 :     103183 : }
     312                 :            : 
     313                 :      15934 : void pl_str_builder_concat(pl_str_builder b, const pl_str_builder append)
     314                 :            : {
     315   [ +  +  +  + ]:      15934 :     PL_ARRAY_CONCAT(b, b->templates, append->templates);
     316                 :      15934 :     pl_str_append_raw(b, &b->args, append->args.buf, append->args.len);
     317                 :      15934 : }
     318                 :            : 
     319                 :       9274 : static size_t template_str_ptr(void *alloc, pl_str *buf, const uint8_t *args)
     320                 :            : {
     321                 :            :     const char *str;
     322                 :            :     memcpy(&str, args, sizeof(str));
     323                 :       9274 :     pl_str_append_raw(alloc, buf, str, strlen(str));
     324                 :       9274 :     return sizeof(str);
     325                 :            : }
     326                 :            : 
     327                 :      29603 : void pl_str_builder_const_str(pl_str_builder b, const char *str)
     328                 :            : {
     329                 :      29603 :     pl_str_builder_append(b, template_str_ptr, &str, sizeof(str));
     330                 :      29603 : }
     331                 :            : 
     332                 :         19 : static size_t template_str(void *alloc, pl_str *buf, const uint8_t *args)
     333                 :            : {
     334                 :            :     pl_str str;
     335                 :            :     memcpy(&str.len, args, sizeof(str.len));
     336                 :         19 :     pl_str_append_raw(alloc, buf, args + sizeof(str.len), str.len);
     337                 :         19 :     return sizeof(str.len) + str.len;
     338                 :            : }
     339                 :            : 
     340                 :         19 : void pl_str_builder_str(pl_str_builder b, const pl_str str)
     341                 :            : {
     342                 :         19 :     pl_str_builder_append(b, template_str, &str.len, sizeof(str.len));
     343                 :         19 :     pl_str_append_raw(b, &b->args, str.buf, str.len);
     344                 :         19 : }
     345                 :            : 
     346                 :      66547 : void pl_str_builder_printf_c(pl_str_builder b, const char *fmt, ...)
     347                 :            : {
     348                 :            :     va_list ap;
     349                 :      66547 :     va_start(ap, fmt);
     350                 :      66547 :     pl_str_builder_vprintf_c(b, fmt, ap);
     351                 :      66547 :     va_end(ap);
     352                 :      66547 : }
     353                 :            : 
     354                 :      25977 : static size_t template_printf(void *alloc, pl_str *str, const uint8_t *args)
     355                 :            : {
     356                 :            :     const char *fmt;
     357                 :            :     memcpy(&fmt, args, sizeof(fmt));
     358                 :      25977 :     args += sizeof(fmt);
     359                 :            : 
     360                 :      25977 :     return sizeof(fmt) + pl_str_append_memprintf_c(alloc, str, fmt, args);
     361                 :            : }
     362                 :            : 
     363                 :      66547 : void pl_str_builder_vprintf_c(pl_str_builder b, const char *fmt, va_list ap)
     364                 :            : {
     365                 :      66547 :     pl_str_builder_append(b, template_printf, &fmt, sizeof(fmt));
     366                 :            : 
     367                 :            :     // Push all of the variadic arguments directly onto `b->args`
     368         [ +  + ]:     220864 :     for (const char *c; (c = strchr(fmt, '%')) != NULL; fmt = c + 1) {
     369                 :     154317 :         c++;
     370   [ +  +  +  +  :     154317 :         switch (c[0]) {
          +  +  +  +  +  
                   +  - ]
     371                 :            : #define WRITE(T, x) pl_str_append_raw(b, &b->args, &(T) {x}, sizeof(T))
     372                 :        189 :         case '%': continue;
     373                 :     163503 :         case 'c': WRITE(char,       va_arg(ap, int)); break;
     374                 :      27939 :         case 'd': WRITE(int,        va_arg(ap, int)); break;
     375                 :       2770 :         case 'u': WRITE(unsigned,   va_arg(ap, unsigned)); break;
     376                 :       7530 :         case 'f': WRITE(double,     va_arg(ap, double)); break;
     377                 :      70942 :         case 'h':
     378         [ -  + ]:      70942 :             assert(c[1] == 'x');
     379                 :      70942 :             WRITE(unsigned short, va_arg(ap, unsigned));
     380                 :      70942 :             c++;
     381                 :      70942 :             break;
     382                 :        144 :         case 'l':
     383         [ -  + ]:        144 :             assert(c[1] == 'l');
     384      [ +  +  - ]:        144 :             switch (c[2]) {
     385                 :        143 :             case 'u': WRITE(long long unsigned, va_arg(ap, long long unsigned)); break;
     386                 :          1 :             case 'd': WRITE(long long int,      va_arg(ap, long long int)); break;
     387                 :          0 :             default: abort();
     388                 :            :             }
     389                 :        144 :             c += 2;
     390                 :        144 :             break;
     391                 :       2364 :         case 'z':
     392         [ -  + ]:       2364 :             assert(c[1] == 'u');
     393                 :       2364 :             WRITE(size_t, va_arg(ap, size_t));
     394                 :       2364 :             c++;
     395                 :       2364 :             break;
     396                 :      32827 :         case 's': {
     397                 :      32827 :             pl_str str = pl_str0(va_arg(ap, const char *));
     398                 :      32827 :             pl_str_append(b, &b->args, str);
     399                 :      32827 :             b->args.len++; // expand to include \0 byte (from pl_str_append)
     400                 :            :             break;
     401                 :            :         }
     402                 :        237 :         case '.': {
     403         [ -  + ]:        237 :             assert(c[1] == '*');
     404         [ -  + ]:        237 :             assert(c[2] == 's');
     405                 :        237 :             int len = va_arg(ap, int);
     406                 :        237 :             const char *str = va_arg(ap, const char *);
     407                 :        237 :             WRITE(int, len);
     408                 :        237 :             pl_str_append_raw(b, &b->args, str, len);
     409                 :        237 :             c += 2;
     410                 :            :             break;
     411                 :            :         }
     412                 :          0 :         default:
     413                 :          0 :             fprintf(stderr, "Invalid conversion character: '%c'!\n", c[0]);
     414                 :          0 :             abort();
     415                 :            :         }
     416                 :            : #undef WRITE
     417                 :            :     }
     418                 :      66547 : }

Generated by: LCOV version 1.16