LCOV - code coverage report
Current view: top level - lib - lwan-strbuf.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 118 185 63.8 %
Date: 2023-04-18 16:19:03 Functions: 19 25 76.0 %

          Line data    Source code
       1             : /*
       2             :  * lwan - web server
       3             :  * Copyright (c) 2012 L. A. F. Pereira <l@tia.mat.br>
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU General Public License
       7             :  * as published by the Free Software Foundation; either version 2
       8             :  * of the License, or any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with this program; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
      18             :  * USA.
      19             :  */
      20             : 
      21             : #define _GNU_SOURCE
      22             : #include <errno.h>
      23             : #include <fcntl.h>
      24             : #include <limits.h>
      25             : #include <stdarg.h>
      26             : #include <stddef.h>
      27             : #include <stdlib.h>
      28             : #include <string.h>
      29             : #include <sys/stat.h>
      30             : 
      31             : #include "lwan-private.h"
      32             : 
      33             : static const unsigned int BUFFER_MALLOCD = 1 << 0;
      34             : static const unsigned int STRBUF_MALLOCD = 1 << 1;
      35             : static const unsigned int BUFFER_FIXED = 1 << 2;
      36             : 
      37        3009 : static inline size_t align_size(size_t unaligned_size)
      38             : {
      39        3009 :     const size_t aligned_size = lwan_nextpow2(unaligned_size);
      40             : 
      41        3009 :     if (UNLIKELY(unaligned_size >= aligned_size))
      42           0 :         return 0;
      43             : 
      44        3009 :     return aligned_size;
      45             : }
      46             : 
      47       64006 : static bool grow_buffer_if_needed(struct lwan_strbuf *s, size_t size)
      48             : {
      49       64006 :     if (s->flags & BUFFER_FIXED)
      50           9 :         return size < s->capacity;
      51             : 
      52       63997 :     if (!(s->flags & BUFFER_MALLOCD)) {
      53        1253 :         const size_t aligned_size = align_size(LWAN_MAX(size + 1, s->used));
      54        1253 :         if (UNLIKELY(!aligned_size))
      55           0 :             return false;
      56             : 
      57        1253 :         char *buffer = malloc(aligned_size);
      58        1253 :         if (UNLIKELY(!buffer))
      59           0 :             return false;
      60             : 
      61        1253 :         memcpy(buffer, s->buffer, s->used);
      62        1253 :         buffer[s->used + 1] = '\0';
      63             : 
      64        1253 :         s->flags |= BUFFER_MALLOCD;
      65        1253 :         s->buffer = buffer;
      66        1253 :         s->capacity = aligned_size;
      67             : 
      68        1253 :         return true;
      69             :     }
      70             : 
      71       62744 :     if (UNLIKELY(s->capacity < size)) {
      72             :         char *buffer;
      73        1756 :         const size_t aligned_size = align_size(size + 1);
      74             : 
      75        1756 :         if (UNLIKELY(!aligned_size))
      76           0 :             return false;
      77             : 
      78        1756 :         if (s->used == 0) {
      79             :             /* Avoid memcpy() inside realloc() if we were not using the
      80             :              * allocated buffer at this point.  */
      81           0 :             buffer = malloc(aligned_size);
      82             : 
      83           0 :             if (UNLIKELY(!buffer))
      84           0 :                 return false;
      85             : 
      86           0 :             free(s->buffer);
      87           0 :             buffer[0] = '\0';
      88             :         } else {
      89        1756 :             buffer = realloc(s->buffer, aligned_size);
      90             : 
      91        1756 :             if (UNLIKELY(!buffer))
      92           0 :                 return false;
      93             :         }
      94             : 
      95        1756 :         s->buffer = buffer;
      96        1756 :         s->capacity = aligned_size;
      97             :     }
      98             : 
      99       62744 :     return true;
     100             : }
     101             : 
     102        2152 : bool lwan_strbuf_init_with_size(struct lwan_strbuf *s, size_t size)
     103             : {
     104        2152 :     if (UNLIKELY(!s))
     105           0 :         return false;
     106             : 
     107        2152 :     *s = LWAN_STRBUF_STATIC_INIT;
     108             : 
     109        2152 :     if (size) {
     110           2 :         if (UNLIKELY(!grow_buffer_if_needed(s, size)))
     111           0 :             return false;
     112             : 
     113           2 :         s->buffer[0] = '\0';
     114             :     }
     115             : 
     116        2152 :     return true;
     117             : }
     118             : 
     119           2 : bool lwan_strbuf_init_with_fixed_buffer(struct lwan_strbuf *s,
     120             :                                         void *buffer,
     121             :                                         size_t size)
     122             : {
     123           2 :     if (UNLIKELY(!s))
     124           0 :         return false;
     125             : 
     126           2 :     *s = (struct lwan_strbuf) {
     127             :         .capacity = size,
     128             :         .used = 0,
     129             :         .buffer = buffer,
     130             :         .flags = BUFFER_FIXED,
     131             :     };
     132             : 
     133           2 :     return true;
     134             : }
     135             : 
     136        2150 : ALWAYS_INLINE bool lwan_strbuf_init(struct lwan_strbuf *s)
     137             : {
     138        2150 :     return lwan_strbuf_init_with_size(s, 0);
     139             : }
     140             : 
     141           0 : struct lwan_strbuf *lwan_strbuf_new_with_size(size_t size)
     142             : {
     143           0 :     struct lwan_strbuf *s = malloc(sizeof(*s));
     144             : 
     145           0 :     if (UNLIKELY(!lwan_strbuf_init_with_size(s, size))) {
     146           0 :         free(s);
     147             : 
     148           0 :         return NULL;
     149             :     }
     150             : 
     151           0 :     s->flags |= STRBUF_MALLOCD;
     152             : 
     153           0 :     return s;
     154             : }
     155             : 
     156           0 : struct lwan_strbuf *lwan_strbuf_new_with_fixed_buffer(size_t size)
     157             : {
     158           0 :     struct lwan_strbuf *s = malloc(sizeof(*s) + size + 1);
     159             : 
     160           0 :     if (UNLIKELY(!lwan_strbuf_init_with_fixed_buffer(s, s + 1, size))) {
     161           0 :         free(s);
     162             : 
     163           0 :         return NULL;
     164             :     }
     165             : 
     166           0 :     s->flags |= STRBUF_MALLOCD;
     167             : 
     168           0 :     return s;
     169             : }
     170             : 
     171           0 : ALWAYS_INLINE struct lwan_strbuf *lwan_strbuf_new(void)
     172             : {
     173           0 :     return lwan_strbuf_new_with_size(0);
     174             : }
     175             : 
     176        1601 : ALWAYS_INLINE struct lwan_strbuf *lwan_strbuf_new_static(const char *str,
     177             :                                                          size_t size)
     178             : {
     179        1601 :     struct lwan_strbuf *s = malloc(sizeof(*s));
     180             : 
     181        1601 :     if (UNLIKELY(!s))
     182           0 :         return NULL;
     183             : 
     184        1601 :     *s = (struct lwan_strbuf) {
     185             :         .flags = STRBUF_MALLOCD,
     186             :         .buffer = (char *)str,
     187             :         .used = size,
     188             :         .capacity = size,
     189             :     };
     190             : 
     191        1601 :     return s;
     192             : }
     193             : 
     194        2293 : void lwan_strbuf_free(struct lwan_strbuf *s)
     195             : {
     196        2293 :     if (UNLIKELY(!s))
     197           0 :         return;
     198        2293 :     if (s->flags & BUFFER_MALLOCD) {
     199        1157 :         assert(!(s->flags & BUFFER_FIXED));
     200        1157 :         free(s->buffer);
     201             :     }
     202        2293 :     if (s->flags & STRBUF_MALLOCD)
     203           0 :         free(s);
     204             : }
     205             : 
     206       20111 : bool lwan_strbuf_append_char(struct lwan_strbuf *s, const char c)
     207             : {
     208       20111 :     if (UNLIKELY(!grow_buffer_if_needed(s, s->used + 2)))
     209           0 :         return false;
     210             : 
     211       20111 :     s->buffer[s->used++] = c;
     212       20111 :     s->buffer[s->used] = '\0';
     213             : 
     214       20111 :     return true;
     215             : }
     216             : 
     217       43539 : bool lwan_strbuf_append_str(struct lwan_strbuf *s1, const char *s2, size_t sz)
     218             : {
     219       43539 :     if (UNLIKELY(!grow_buffer_if_needed(s1, s1->used + sz + 2)))
     220           0 :         return false;
     221             : 
     222       43539 :     memcpy(s1->buffer + s1->used, s2, sz);
     223       43539 :     s1->used += sz;
     224       43539 :     s1->buffer[s1->used] = '\0';
     225             : 
     226       43539 :     return true;
     227             : }
     228             : 
     229         158 : bool lwan_strbuf_set_static(struct lwan_strbuf *s1, const char *s2, size_t sz)
     230             : {
     231         158 :     if (s1->flags & BUFFER_MALLOCD)
     232           0 :         free(s1->buffer);
     233             : 
     234         158 :     s1->buffer = (char *)s2;
     235         158 :     s1->used = s1->capacity = sz;
     236         158 :     s1->flags &= ~(BUFFER_MALLOCD | BUFFER_FIXED);
     237             : 
     238         158 :     return true;
     239             : }
     240             : 
     241         301 : bool lwan_strbuf_set(struct lwan_strbuf *s1, const char *s2, size_t sz)
     242             : {
     243         301 :     if (UNLIKELY(!grow_buffer_if_needed(s1, sz + 1)))
     244           0 :         return false;
     245             : 
     246         301 :     memcpy(s1->buffer, s2, sz);
     247         301 :     s1->used = sz;
     248         301 :     s1->buffer[sz] = '\0';
     249             : 
     250         301 :     return true;
     251             : }
     252             : 
     253             : static ALWAYS_INLINE bool
     254             : internal_printf(struct lwan_strbuf *s1,
     255             :                 bool (*save_str)(struct lwan_strbuf *, const char *, size_t),
     256             :                 const char *fmt,
     257             :                 va_list values)
     258             : {
     259             :     char *s2;
     260             :     int len;
     261             : 
     262         541 :     if (UNLIKELY((len = vasprintf(&s2, fmt, values)) < 0))
     263           0 :         return false;
     264             : 
     265         541 :     bool success = save_str(s1, s2, (size_t)len);
     266         541 :     free(s2);
     267             : 
     268         541 :     return success;
     269             : }
     270             : 
     271         296 : bool lwan_strbuf_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
     272             : {
     273         296 :     return internal_printf(s, lwan_strbuf_set, fmt, ap);
     274             : }
     275             : 
     276         296 : bool lwan_strbuf_printf(struct lwan_strbuf *s, const char *fmt, ...)
     277             : {
     278             :     bool could_printf;
     279             :     va_list values;
     280             : 
     281         296 :     va_start(values, fmt);
     282         296 :     could_printf = lwan_strbuf_vprintf(s, fmt, values);
     283         296 :     va_end(values);
     284             : 
     285         296 :     return could_printf;
     286             : }
     287             : 
     288         245 : bool lwan_strbuf_append_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
     289             : {
     290         245 :     return internal_printf(s, lwan_strbuf_append_str, fmt, ap);
     291             : }
     292             : 
     293         245 : bool lwan_strbuf_append_printf(struct lwan_strbuf *s, const char *fmt, ...)
     294             : {
     295             :     bool could_printf;
     296             :     va_list values;
     297             : 
     298         245 :     va_start(values, fmt);
     299         245 :     could_printf = lwan_strbuf_append_vprintf(s, fmt, values);
     300         245 :     va_end(values);
     301             : 
     302         245 :     return could_printf;
     303             : }
     304             : 
     305          53 : bool lwan_strbuf_grow_to(struct lwan_strbuf *s, size_t new_size)
     306             : {
     307          53 :     return grow_buffer_if_needed(s, new_size + 1);
     308             : }
     309             : 
     310           0 : bool lwan_strbuf_grow_by(struct lwan_strbuf *s, size_t offset)
     311             : {
     312             :     size_t new_size;
     313             : 
     314           0 :     if (__builtin_add_overflow(offset, s->used, &new_size))
     315           0 :         return false;
     316             : 
     317           0 :     return lwan_strbuf_grow_to(s, new_size);
     318             : }
     319             : 
     320       82644 : void lwan_strbuf_reset(struct lwan_strbuf *s)
     321             : {
     322       82644 :     if (s->flags & BUFFER_MALLOCD) {
     323       78029 :         s->buffer[0] = '\0';
     324             :     } else {
     325        4615 :         s->buffer = "";
     326        4615 :         s->capacity = 0;
     327             :     }
     328             : 
     329       82644 :     s->used = 0;
     330       82644 : }
     331             : 
     332         268 : void lwan_strbuf_reset_trim(struct lwan_strbuf *s, size_t trim_thresh)
     333             : {
     334         268 :     if (s->flags & BUFFER_MALLOCD && s->capacity > trim_thresh) {
     335           0 :         free(s->buffer);
     336           0 :         s->flags &= ~BUFFER_MALLOCD;
     337             :     }
     338             : 
     339         268 :     return lwan_strbuf_reset(s);
     340             : }
     341             : 
     342             : /* This function is quite dangerous, so the prototype is only in lwan-private.h */
     343           0 : char *lwan_strbuf_extend_unsafe(struct lwan_strbuf *s, size_t by)
     344             : {
     345           0 :     if (!lwan_strbuf_grow_by(s, by))
     346           0 :         return NULL;
     347             : 
     348           0 :     size_t prev_used = s->used;
     349           0 :     s->used += by;
     350             : 
     351           0 :     return s->buffer + prev_used;
     352             : }
     353             : 
     354           8 : bool lwan_strbuf_init_from_file(struct lwan_strbuf *s, const char *path)
     355             : {
     356           8 :     int fd = open(path, O_RDONLY | O_CLOEXEC);
     357             :     struct stat st;
     358             : 
     359           8 :     if (UNLIKELY(fd < 0))
     360           6 :         return false;
     361             : 
     362           2 :     if (UNLIKELY(fstat(fd, &st) < 0))
     363           0 :         goto error;
     364             : 
     365           2 :     if (UNLIKELY(!lwan_strbuf_init_with_size(s, (size_t)st.st_size)))
     366           0 :         goto error;
     367             : 
     368           2 :     s->used = (size_t)st.st_size;
     369             : 
     370           4 :     for (char *buffer = s->buffer; st.st_size; ) {
     371           2 :         ssize_t n_read = read(fd, buffer, (size_t)st.st_size);
     372             : 
     373           2 :         if (UNLIKELY(n_read < 0)) {
     374           0 :             if (errno == EINTR)
     375           0 :                 continue;
     376           0 :             goto error;
     377             :         }
     378             : 
     379           2 :         buffer += n_read;
     380           2 :         st.st_size -= (off_t)n_read;
     381             :     }
     382             : 
     383           2 :     close(fd);
     384           2 :     return true;
     385             : 
     386           0 : error:
     387           0 :     lwan_strbuf_free(s);
     388           0 :     close(fd);
     389           0 :     return false;
     390             : }
     391             : 
     392           0 : struct lwan_strbuf *lwan_strbuf_new_from_file(const char *path)
     393             : {
     394           0 :     struct lwan_strbuf *strbuf = malloc(sizeof(*strbuf));
     395             : 
     396           0 :     if (!strbuf)
     397           0 :         return NULL;
     398             : 
     399           0 :     if (lwan_strbuf_init_from_file(strbuf, path)) {
     400           0 :         strbuf->flags |= STRBUF_MALLOCD;
     401           0 :         return strbuf;
     402             :     }
     403             : 
     404           0 :     free(strbuf);
     405           0 :     return NULL;
     406             : }

Generated by: LCOV version 1.15-2-gb9d6727