rsys

Basic data structures and low-level features
git clone git://git.meso-star.fr/rsys.git
Log | Files | Refs | README | LICENSE

str.c (5726B)


      1 /* Copyright (C) 2013-2023, 2025 Vincent Forest (vaplv@free.fr)
      2  *
      3  * The RSys library is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published
      5  * by the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * The RSys library is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #define _POSIX_C_SOURCE 200112L /* vsnprintf support */
     17 #include "str.h"
     18 
     19 #include <string.h>
     20 
     21 /*******************************************************************************
     22  * helper function
     23  ******************************************************************************/
     24 static res_T
     25 ensure_allocated(struct str* str, const size_t len, const char keep_old)
     26 {
     27   char* buf = NULL;
     28   const size_t alloc_granularity = 32;
     29   size_t mod = 0;
     30   size_t new_len = 0;
     31   size_t new_size = 0;
     32   ASSERT( str );
     33 
     34   if(len * sizeof(char) <= str->allocated)
     35     return RES_OK;
     36 
     37   mod = len % alloc_granularity;
     38   new_len = !mod ? len : len - mod + alloc_granularity;
     39   new_size = new_len * sizeof(char);
     40   buf = MEM_ALLOC(str->allocator, new_size);
     41   if(!buf)
     42     return RES_MEM_ERR;
     43 
     44   if(keep_old) {
     45     strncpy( buf, str->cstr, new_len - 1);
     46     buf[new_len - 1] = '\0';
     47   }
     48 
     49   str->allocated = new_len * sizeof(char);
     50   if(str->cstr != str->buffer)
     51     MEM_RM(str->allocator, str->cstr);
     52 
     53   str->cstr = buf;
     54   return RES_OK;
     55 }
     56 
     57 /*******************************************************************************
     58  * Exported functions
     59  ******************************************************************************/
     60 res_T
     61 str_set(struct str* str, const char* cstr)
     62 {
     63   size_t cstr_len = 0;
     64   res_T res = 0;
     65   ASSERT(str && cstr);
     66 
     67   cstr_len = strlen(cstr);
     68   res = ensure_allocated(str, cstr_len + 1, 0);
     69   if(res != RES_OK) return res;
     70   strncpy(str->cstr, cstr, cstr_len + 1);
     71   return RES_OK;
     72 }
     73 
     74 res_T
     75 str_insert(struct str* str, const size_t i, const char* cstr)
     76 {
     77   size_t len = 0;
     78   size_t cstr_len = 0;
     79   ASSERT(str);
     80 
     81   len = str_len(str);
     82 
     83   if(i > len)
     84     return RES_BAD_ARG;
     85 
     86   cstr_len = strlen(cstr);
     87   ASSERT(!MEM_AREA_OVERLAP
     88     (str->cstr, str->allocated, cstr, (cstr_len + 1) * sizeof(char)));
     89 
     90   if(i == len) {
     91     return str_append(str, cstr);
     92   } else {
     93     const res_T res = ensure_allocated(str, cstr_len + len + 1, 1);
     94     if(res != RES_OK)
     95       return res;
     96     memmove
     97       (str->cstr + i + cstr_len,
     98        str->cstr + i,
     99        (len - i) * sizeof(char));
    100     memcpy(str->cstr + i, cstr, cstr_len * sizeof(char));
    101     str->cstr[len + cstr_len] = '\0';
    102   }
    103   return RES_OK;
    104 }
    105 
    106 res_T
    107 str_insert_char(struct str* str, const size_t i, const char ch)
    108 {
    109   size_t len = 0;
    110   ASSERT(str);
    111 
    112   len = str_len(str);
    113   if(i > len)
    114     return RES_BAD_ARG;
    115 
    116   if(i == len) {
    117     return str_append_char(str, ch);
    118   } else if(ch == '\0') {
    119     str->cstr[i] = ch;
    120   } else {
    121     const res_T res = ensure_allocated(str, len + 2, 1);
    122     if(res != RES_OK)
    123       return res;
    124     memmove
    125       (str->cstr + i + 1,
    126        str->cstr + i,
    127        (len - i) * sizeof(char));
    128     str->cstr[i] = ch;
    129     str->cstr[len+1] = '\0';
    130   }
    131   return RES_OK;
    132 }
    133 
    134 res_T
    135 str_append(struct str* str, const char* cstr)
    136 {
    137   size_t len = 0;
    138   size_t cstr_len = 0;
    139   res_T res = RES_OK;
    140   ASSERT(str && cstr);
    141 
    142   cstr_len = strlen(cstr);
    143   ASSERT(!MEM_AREA_OVERLAP
    144     (str->cstr, str->allocated, cstr, (cstr_len + 1) * sizeof(char)));
    145 
    146   len = str_len(str);
    147   res = ensure_allocated(str, cstr_len + len + 1, 1);
    148   if(res != RES_OK)
    149     return res;
    150 
    151   memcpy(str->cstr + len, cstr, cstr_len * sizeof(char));
    152   str->cstr[len + cstr_len] = '\0';
    153   return RES_OK;
    154 }
    155 
    156 res_T
    157 str_append_char(struct str* str, const char ch)
    158 {
    159   size_t len = 0;
    160   res_T res = RES_OK;
    161   ASSERT(str);
    162 
    163   if(ch == '\0')
    164     return RES_OK;
    165 
    166   len = str_len(str);
    167   res = ensure_allocated(str, len + 2, 1);
    168   if(res != RES_OK) return res;
    169 
    170   str->cstr[len+0] = ch;
    171   str->cstr[len+1] = '\0';
    172   return RES_OK;
    173 }
    174 
    175 res_T
    176 str_reserve(struct str* str, const size_t capacity)
    177 {
    178   return ensure_allocated(str, capacity / sizeof(char), 1);
    179 }
    180 
    181 
    182 res_T
    183 str_printf(struct str* str, const char* fmt, ...)
    184 {
    185   va_list ap;
    186   res_T res = RES_OK;
    187   ASSERT(str && fmt);
    188   va_start(ap, fmt);
    189   res = str_vprintf(str, fmt, ap);
    190   va_end(ap);
    191   return res;
    192 }
    193 
    194 res_T
    195 str_append_printf(struct str* str, const char* fmt, ...)
    196 {
    197   va_list ap;
    198   res_T res = RES_OK;
    199   ASSERT(str && fmt);
    200   va_start(ap, fmt);
    201   res = str_append_vprintf(str, fmt, ap);
    202   va_end(ap);
    203   return res;
    204 }
    205 
    206 res_T
    207 str_vprintf(struct str* str, const char* fmt, va_list vargs_list)
    208 {
    209   ASSERT(str && fmt);
    210   str_clear(str);
    211   return str_append_vprintf(str, fmt, vargs_list);
    212 }
    213 
    214 res_T
    215 str_append_vprintf(struct str* str, const char* fmt, va_list vargs_list)
    216 {
    217   va_list ap;
    218   size_t flen; /* Length of the formatted message */
    219   size_t slen; /* Length of the string */
    220   res_T res = RES_OK;
    221   ASSERT(str && fmt);
    222 
    223   slen = str_len(str);
    224 
    225   VA_COPY(ap, vargs_list);
    226   flen = (size_t)vsnprintf(str->cstr + slen, str->allocated - slen, fmt, ap);
    227   va_end(ap);
    228 
    229   if(slen + flen >= str->allocated) {
    230     res = ensure_allocated(str, slen + flen + 1/* Null char */, 1);
    231     if(res != RES_OK) goto error;
    232 
    233     VA_COPY(ap, vargs_list);
    234     flen = (size_t)vsnprintf(str->cstr + slen, str->allocated - slen, fmt, ap);
    235     va_end(ap);
    236     CHK(slen + flen < str->allocated);
    237   }
    238 
    239 exit:
    240   return res;
    241 error:
    242   goto exit;
    243 }