LCOV - code coverage report
Current view: top level - src - pl_alloc.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 122 140 87.1 %
Date: 2025-03-29 09:04:10 Functions: 16 17 94.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 57 82 69.5 %

           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                 :            : 
      20                 :            : struct header {
      21                 :            : #ifndef NDEBUG
      22                 :            : #define MAGIC 0x20210119LU
      23                 :            :     uint32_t magic;
      24                 :            : #endif
      25                 :            :     size_t size;
      26                 :            :     struct header *parent;
      27                 :            :     struct ext *ext;
      28                 :            : 
      29                 :            :     // Pointer to actual data, for alignment purposes
      30                 :            :     max_align_t data[];
      31                 :            : };
      32                 :            : 
      33                 :            : // Lazily allocated, to save space for leaf allocations and allocations which
      34                 :            : // don't need fancy requirements
      35                 :            : struct ext {
      36                 :            :     size_t num_children;
      37                 :            :     size_t children_size; // total allocated size of `children`
      38                 :            :     struct header *children[];
      39                 :            : };
      40                 :            : 
      41                 :            : #define PTR_OFFSET offsetof(struct header, data)
      42                 :            : #define MAX_ALLOC (SIZE_MAX - PTR_OFFSET)
      43                 :            : #define MINIMUM_CHILDREN 4
      44                 :            : 
      45                 :    1049272 : static inline struct header *get_header(void *ptr)
      46                 :            : {
      47         [ +  + ]:    1049272 :     if (!ptr)
      48                 :            :         return NULL;
      49                 :            : 
      50                 :    1032877 :     struct header *hdr = (struct header *) ((uintptr_t) ptr - PTR_OFFSET);
      51                 :            : #ifndef NDEBUG
      52         [ -  + ]:    1032877 :     assert(hdr->magic == MAGIC);
      53                 :            : #endif
      54                 :            : 
      55                 :            :     return hdr;
      56                 :            : }
      57                 :            : 
      58                 :          0 : static inline void *oom(void)
      59                 :            : {
      60                 :          0 :     fprintf(stderr, "out of memory\n");
      61                 :          0 :     abort();
      62                 :            : }
      63                 :            : 
      64                 :      33157 : static inline struct ext *alloc_ext(struct header *h)
      65                 :            : {
      66         [ +  - ]:      33157 :     if (!h)
      67                 :            :         return NULL;
      68                 :            : 
      69         [ +  + ]:      33157 :     if (!h->ext) {
      70                 :       5347 :         h->ext = malloc(sizeof(struct ext) + MINIMUM_CHILDREN * sizeof(void *));
      71         [ -  + ]:       5347 :         if (!h->ext)
      72                 :          0 :             oom();
      73                 :       5347 :         h->ext->num_children = 0;
      74                 :       5347 :         h->ext->children_size = MINIMUM_CHILDREN;
      75                 :            :     }
      76                 :            : 
      77                 :      33157 :     return h->ext;
      78                 :            : }
      79                 :            : 
      80                 :      42226 : static inline void attach_child(struct header *parent, struct header *child)
      81                 :            : {
      82                 :      42226 :     child->parent = parent;
      83         [ +  + ]:      42226 :     if (!parent)
      84                 :            :         return;
      85                 :            : 
      86                 :            : 
      87                 :      33157 :     struct ext *ext = alloc_ext(parent);
      88         [ +  + ]:      33157 :     if (ext->num_children == ext->children_size) {
      89                 :       1795 :         size_t new_size = ext->children_size * 2;
      90                 :       1795 :         ext = realloc(ext, sizeof(struct ext) + new_size * sizeof(void *));
      91         [ -  + ]:       1795 :         if (!ext)
      92                 :          0 :             oom();
      93                 :       1795 :         ext->children_size = new_size;
      94                 :       1795 :         parent->ext = ext;
      95                 :            :     }
      96                 :            : 
      97                 :      33157 :     ext->children[ext->num_children++] = child;
      98                 :            : }
      99                 :            : 
     100                 :      42226 : static inline void unlink_child(struct header *parent, struct header *child)
     101                 :            : {
     102                 :      42226 :     child->parent = NULL;
     103         [ +  + ]:      42226 :     if (!parent)
     104                 :            :         return;
     105                 :            : 
     106                 :       5481 :     struct ext *ext = parent->ext;
     107         [ +  - ]:     136739 :     for (size_t i = 0; i < ext->num_children; i++) {
     108         [ +  + ]:     136739 :         if (ext->children[i] == child) {
     109                 :       5481 :             memmove(&ext->children[i], &ext->children[i + 1],
     110                 :       5481 :                     (--ext->num_children - i) * sizeof(ext->children[0]));
     111                 :       5481 :             return;
     112                 :            :         }
     113                 :            :     }
     114                 :            : 
     115                 :          0 :     assert(!"unlinking orphaned child?");
     116                 :            : }
     117                 :            : 
     118                 :      24556 : void *pl_alloc(void *parent, size_t size)
     119                 :            : {
     120         [ -  + ]:      24556 :     if (size >= MAX_ALLOC)
     121                 :          0 :         return oom();
     122                 :            : 
     123                 :      24556 :     struct header *h = malloc(PTR_OFFSET + size);
     124         [ -  + ]:      24556 :     if (!h)
     125                 :          0 :         return oom();
     126                 :            : 
     127                 :            : #ifndef NDEBUG
     128                 :      24556 :     h->magic = MAGIC;
     129                 :            : #endif
     130                 :      24556 :     h->size = size;
     131                 :      24556 :     h->ext = NULL;
     132                 :            : 
     133                 :      24556 :     attach_child(get_header(parent), h);
     134                 :      24556 :     return h->data;
     135                 :            : }
     136                 :            : 
     137                 :      14517 : void *pl_zalloc(void *parent, size_t size)
     138                 :            : {
     139         [ -  + ]:      14517 :     if (size >= MAX_ALLOC)
     140                 :          0 :         return oom();
     141                 :            : 
     142                 :      14517 :     struct header *h = calloc(1, PTR_OFFSET + size);
     143         [ -  + ]:      14517 :     if (!h)
     144                 :          0 :         return oom();
     145                 :            : 
     146                 :            : #ifndef NDEBUG
     147                 :      14517 :     h->magic = MAGIC;
     148                 :            : #endif
     149                 :      14517 :     h->size = size;
     150                 :            : 
     151                 :      14517 :     attach_child(get_header(parent), h);
     152                 :      14517 :     return h->data;
     153                 :            : }
     154                 :            : 
     155                 :       6583 : void *pl_realloc(void *parent, void *ptr, size_t size)
     156                 :            : {
     157         [ -  + ]:       6583 :     if (size >= MAX_ALLOC)
     158                 :          0 :         return oom();
     159         [ +  + ]:       6583 :     if (!ptr)
     160                 :       4692 :         return pl_alloc(parent, size);
     161                 :            : 
     162                 :       1891 :     struct header *h = get_header(ptr);
     163         [ -  + ]:       1891 :     assert(get_header(parent) == h->parent);
     164         [ +  - ]:       1891 :     if (h->size == size)
     165                 :            :         return ptr;
     166                 :            : 
     167                 :            :     struct header *old_h = h;
     168                 :       1891 :     h = realloc(h, PTR_OFFSET + size);
     169         [ -  + ]:       1891 :     if (!h)
     170                 :          0 :         return oom();
     171                 :            : 
     172                 :       1891 :     h->size = size;
     173                 :            : 
     174         [ +  + ]:       1891 :     if (h != old_h) {
     175         [ +  - ]:       1409 :         if (h->parent) {
     176                 :       1409 :             struct ext *ext = h->parent->ext;
     177         [ +  - ]:       3048 :             for (size_t i = 0; i < ext->num_children; i++) {
     178         [ +  + ]:       3048 :                 if (ext->children[i] == old_h) {
     179                 :       1409 :                     ext->children[i] = h;
     180                 :       1409 :                     goto done_reparenting;
     181                 :            :                 }
     182                 :            :             }
     183                 :          0 :             assert(!"reallocating orphaned child?");
     184                 :            :         }
     185                 :          0 : done_reparenting:
     186                 :            : 
     187         [ -  + ]:       1409 :         if (h->ext) {
     188         [ #  # ]:          0 :             for (size_t i = 0; i < h->ext->num_children; i++)
     189                 :          0 :                 h->ext->children[i]->parent = h;
     190                 :            :         }
     191                 :            :     }
     192                 :            : 
     193                 :       1891 :     return h->data;
     194                 :            : }
     195                 :            : 
     196                 :      40349 : void pl_free(void *ptr)
     197                 :            : {
     198                 :      40349 :     struct header *h = get_header(ptr);
     199         [ +  + ]:      40349 :     if (!h)
     200                 :            :         return;
     201                 :            : 
     202                 :      39073 :     pl_free_children(ptr);
     203                 :      39073 :     unlink_child(h->parent, h);
     204                 :            : 
     205                 :      39073 :     free(h->ext);
     206                 :      39073 :     free(h);
     207                 :            : }
     208                 :            : 
     209                 :      47400 : void pl_free_children(void *ptr)
     210                 :            : {
     211                 :      47400 :     struct header *h = get_header(ptr);
     212   [ +  -  +  + ]:      47400 :     if (!h || !h->ext)
     213                 :            :         return;
     214                 :            : 
     215                 :            : #ifndef NDEBUG
     216                 :            :     // this detects recursive hierarchies
     217                 :      11062 :     h->magic = 0;
     218                 :            : #endif
     219                 :            : 
     220         [ +  + ]:      38738 :     for (size_t i = 0; i < h->ext->num_children; i++) {
     221                 :      27676 :         h->ext->children[i]->parent = NULL; // prevent recursive access
     222                 :      27676 :         pl_free(h->ext->children[i]->data);
     223                 :            :     }
     224                 :      11062 :     h->ext->num_children = 0;
     225                 :            : 
     226                 :            : #ifndef NDEBUG
     227                 :      11062 :     h->magic = MAGIC;
     228                 :            : #endif
     229                 :            : }
     230                 :            : 
     231                 :     910439 : size_t pl_get_size(const void *ptr)
     232                 :            : {
     233                 :     910439 :     const struct header *h = get_header((void *) ptr);
     234         [ +  + ]:     910439 :     return h ? h->size : 0;
     235                 :            : }
     236                 :            : 
     237                 :       5074 : void *pl_steal(void *parent, void *ptr)
     238                 :            : {
     239                 :       5074 :     struct header *h = get_header(ptr);
     240         [ +  + ]:       5074 :     if (!h)
     241                 :            :         return NULL;
     242                 :            : 
     243                 :       3155 :     struct header *new_par = get_header(parent);
     244         [ +  + ]:       3155 :     if (new_par != h->parent) {
     245                 :       3153 :         unlink_child(h->parent, h);
     246                 :       3153 :         attach_child(new_par, h);
     247                 :            :     }
     248                 :            : 
     249                 :       3155 :     return h->data;
     250                 :            : }
     251                 :            : 
     252                 :       6343 : void *pl_memdup(void *parent, const void *ptr, size_t size)
     253                 :            : {
     254         [ +  + ]:       6343 :     if (!size)
     255                 :            :         return NULL;
     256                 :            : 
     257                 :       5624 :     void *new = pl_alloc(parent, size);
     258         [ -  + ]:       5624 :     if (!new)
     259                 :          0 :         return oom();
     260                 :            : 
     261         [ -  + ]:       5624 :     assert(ptr);
     262                 :            :     memcpy(new, ptr, size);
     263                 :       5624 :     return new;
     264                 :            : }
     265                 :            : 
     266                 :       3407 : char *pl_str0dup0(void *parent, const char *str)
     267                 :            : {
     268         [ +  + ]:       3407 :     if (!str)
     269                 :            :         return NULL;
     270                 :            : 
     271                 :       3319 :     return pl_memdup(parent, str, strlen(str) + 1);
     272                 :            : }
     273                 :            : 
     274                 :         55 : char *pl_strndup0(void *parent, const char *str, size_t size)
     275                 :            : {
     276         [ +  - ]:         55 :     if (!str)
     277                 :            :         return NULL;
     278                 :            : 
     279                 :         55 :     size_t str_size = strnlen(str, size);
     280                 :         55 :     char *new = pl_alloc(parent, str_size + 1);
     281         [ -  + ]:         55 :     if (!new)
     282                 :          0 :         return oom();
     283                 :            :     memcpy(new, str, str_size);
     284                 :         55 :     new[str_size] = '\0';
     285                 :         55 :     return new;
     286                 :            : }
     287                 :            : 
     288                 :       2604 : char *pl_asprintf(void *parent, const char *fmt, ...)
     289                 :            : {
     290                 :            :     char *str;
     291                 :            :     va_list ap;
     292                 :       2604 :     va_start(ap, fmt);
     293                 :       2604 :     str = pl_vasprintf(parent, fmt, ap);
     294                 :       2604 :     va_end(ap);
     295                 :       2604 :     return str;
     296                 :            : }
     297                 :            : 
     298                 :       3723 : char *pl_vasprintf(void *parent, const char *fmt, va_list ap)
     299                 :            : {
     300                 :            :     // First, we need to determine the size that will be required for
     301                 :            :     // printing the entire string. Do this by making a copy of the va_list
     302                 :            :     // and printing it to a null buffer.
     303                 :            :     va_list copy;
     304         [ +  - ]:       3723 :     va_copy(copy, ap);
     305                 :            :     int size = vsnprintf(NULL, 0, fmt, copy);
     306                 :       3723 :     va_end(copy);
     307         [ +  - ]:       3723 :     if (size < 0)
     308                 :            :         return NULL;
     309                 :            : 
     310                 :       3723 :     char *str = pl_alloc(parent, size + 1);
     311                 :            :     vsnprintf(str, size + 1, fmt, ap);
     312                 :       3723 :     return str;
     313                 :            : }

Generated by: LCOV version 1.16