URI:
       memory.c - ledit - Text editor (WIP)
  HTML git clone git://lumidify.org/ledit.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ledit.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       memory.c (6618B)
       ---
            1 #include <stdio.h>
            2 #include <stdint.h>
            3 #include <stdlib.h>
            4 #include <string.h>
            5 
            6 #include "assert.h"
            7 #include "memory.h"
            8 #include "cleanup.h"
            9 
           10 static void
           11 fatal_err(const char *msg) {
           12         fprintf(stderr, "%s", msg);
           13         /* FIXME: maybe don't cleanup here - it will probably fail anyways */
           14         ledit_cleanup();
           15         exit(1);
           16 }
           17 
           18 void
           19 err_overflow_impl(const char *file, int line, const char *func) {
           20         (void)fprintf(stderr, "Integer overflow: file \"%s\", line %d, function \"%s\"\n", file, line, func);
           21         ledit_emergencydump(file, line, func, "Integer overflow");
           22         abort();
           23 }
           24 
           25 /* FIXME: should these perform emergencydump instead of just
           26    fatal_err? It probably isn't of much use when there isn't
           27    even any memory left. */
           28 char *
           29 ledit_strdup(const char *s) {
           30         char *str = strdup(s);
           31         ledit_assert(str && "Out of memory.");
           32         if (!str)
           33                 fatal_err("Out of memory.\n");
           34         return str;
           35 }
           36 
           37 char *
           38 ledit_strndup(const char *s, size_t n) {
           39         char *str = strndup(s, n);
           40         if (!str)
           41                 fatal_err("Out of memory.\n");
           42         return str;
           43 }
           44 
           45 void *
           46 ledit_malloc(size_t size) {
           47         void *ptr = malloc(size);
           48         if (!ptr)
           49                 fatal_err("Out of memory.\n");
           50         return ptr;
           51 }
           52 
           53 void *
           54 ledit_calloc(size_t nmemb, size_t size) {
           55         void *ptr = calloc(nmemb, size);
           56         if (!ptr)
           57                 fatal_err("Out of memory.\n");
           58         return ptr;
           59 }
           60 
           61 void *
           62 ledit_realloc(void *ptr, size_t size) {
           63         void *new_ptr = realloc(ptr, size);
           64         if (!new_ptr)
           65                 fatal_err("Out of memory.\n");
           66         return new_ptr;
           67 }
           68 
           69 /* Concatenate the two given strings and return the result.
           70    This allocates new memory for the result string, unlike
           71    the actual strcat. Aborts program on error */
           72 char *
           73 ledit_strcat(const char *str1, const char *str2) {
           74         size_t len1, len2;
           75         char *ret;
           76 
           77         len1 = strlen(str1);
           78         len2 = strlen(str2);
           79         ret = ledit_malloc(len1 + len2 + 1);
           80         strcpy(ret, str1);
           81         strcpy(ret + len1, str2);
           82 
           83         return ret;
           84 }
           85 
           86 char *
           87 print_fmt(char *fmt, ...) {
           88         va_list args;
           89         va_start(args, fmt);
           90         int len = vsnprintf(NULL, 0, fmt, args);
           91         /* FIXME: what should be done on error? */
           92         if (len < 0)
           93                 fatal_err("Error in vsnprintf called from print_fmt");
           94         /* FIXME: overflow */
           95         char *str = ledit_malloc(len + 1);
           96         va_end(args);
           97         va_start(args, fmt);
           98         vsnprintf(str, len + 1, fmt, args);
           99         va_end(args);
          100         return str;
          101 }
          102 
          103 /*
          104  * This (reallocarray) is from OpenBSD (adapted to exit on error):
          105  * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
          106  */
          107 
          108 /*
          109  * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
          110  * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
          111  */
          112 #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
          113 
          114 void *
          115 ledit_reallocarray(void *optr, size_t nmemb, size_t size)
          116 {
          117         if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
          118             nmemb > 0 && SIZE_MAX / nmemb < size) {
          119                 err_overflow();
          120         }
          121         return ledit_realloc(optr, size * nmemb);
          122 }
          123 
          124 void
          125 move_gap(
          126     void *array, size_t elem_size, size_t index,
          127     size_t gap, size_t cap, size_t len,
          128     size_t *new_gap_ret) {
          129         ledit_assert(array != NULL);
          130         ledit_assert(index <= len);
          131         ledit_assert(len <= cap);
          132         char *carray = (char *)array; /* cast to char * for pointer arithmetic */
          133         /* since the array has size cap * elem_size, it is assumed that no overflow happens */
          134         if (index > gap) {
          135                 /* move piece between end of original gap and
          136                    index to beginning of original gap */
          137                 memmove(
          138                     carray + gap * elem_size,
          139                     carray + (gap + cap - len) * elem_size,
          140                     (index - gap) * elem_size
          141                 );
          142         } else if (index < gap) {
          143                 /* move piece between index and original gap to
          144                    end of original gap */
          145                 memmove(
          146                     carray + (index + cap - len) * elem_size,
          147                     carray + index * elem_size,
          148                     (gap - index) * elem_size
          149                 );
          150         }
          151         if (new_gap_ret)
          152                 *new_gap_ret = index;
          153 }
          154 
          155 /* FIXME: replace with macro version that takes type parameter in order
          156    to avoid errors with elem_size */
          157 /* This is almost certainly premature optimization and maybe
          158    not optimization at all. */
          159 void *
          160 resize_and_move_gap(
          161     void *array, size_t elem_size,
          162     size_t old_gap, size_t old_cap, size_t len,
          163     size_t min_size, size_t index,
          164     size_t *new_gap_ret, size_t *new_cap_ret) {
          165         ledit_assert(array != NULL || (len == 0 && old_cap == 0));
          166         ledit_assert(index <= len);
          167         ledit_assert(len <= old_cap);
          168         ledit_assert(old_gap <= len);
          169         size_t gap_size = old_cap - len;
          170         size_t new_cap = ideal_array_size(old_cap, min_size);;
          171         if (new_cap >= old_cap) {
          172                 if (new_cap > old_cap)
          173                         array = ledit_reallocarray(array, new_cap, elem_size);
          174                 char *carray = (char*)array; /* cast to char to do pointer arithmetic */
          175                 /* we already know new_cap * elem_size does not wrap around because array
          176                    is of that size, so all the other multiplications here should be safe
          177                    (at least that's what I think, but I may be wrong) */
          178                 if (index > old_gap) {
          179                         /* move piece between end of original gap and index to
          180                            beginning of original gap */
          181                         memmove(
          182                             carray + old_gap * elem_size,
          183                             carray + (old_gap + gap_size) * elem_size,
          184                             (index - old_gap) * elem_size
          185                         );
          186                         /* move piece after index to end of buffer */
          187                         memmove(
          188                             carray + (new_cap - (len - index)) * elem_size,
          189                             carray + (index + gap_size) * elem_size,
          190                             (len - index) * elem_size
          191                         );
          192                 } else if (index < old_gap) {
          193                         /* move piece after original gap to end of buffer */
          194                         memmove(
          195                             carray + (new_cap - (len - old_gap)) * elem_size,
          196                             carray + (old_gap + gap_size) * elem_size,
          197                             (len - old_gap) * elem_size
          198                         );
          199                         /* move piece between index and original gap to end */
          200                         memmove(
          201                             carray + (new_cap - len + index) * elem_size,
          202                             carray + index * elem_size,
          203                             (old_gap - index) * elem_size
          204                         );
          205                 } else {
          206                         /* move piece after original gap to end of buffer */
          207                         memmove(
          208                             carray + (new_cap - (len - old_gap)) * elem_size,
          209                             carray + (old_gap + gap_size) * elem_size,
          210                             (len - old_gap) * elem_size
          211                         );
          212                 }
          213         } else {
          214                 /* otherwise, parts may be cut off */
          215                 ledit_assert(min_size >= len);
          216                 /* FIXME: optimize this */
          217                 if (array)
          218                         move_gap(array, elem_size, len, old_gap, old_cap, len, NULL);
          219                 array = ledit_reallocarray(array, new_cap, elem_size);
          220                 move_gap(array, elem_size, index, len, new_cap, len, NULL);
          221         }
          222         if (new_gap_ret)
          223                 *new_gap_ret = index;
          224         if (new_cap_ret)
          225                 *new_cap_ret = new_cap;
          226         return array;
          227 }
          228 
          229 /* FIXME: maybe don't double when already very large? */
          230 /* FIXME: better start size when old == 0? */
          231 size_t
          232 ideal_array_size(size_t old, size_t needed) {
          233         size_t ret = old;
          234         if (old < needed)
          235                 ret = old * 2 > needed ? old * 2 : needed;
          236         else if (needed * 4 < old)
          237                 ret = old / 2;
          238         if (ret == 0)
          239                 ret = 1; /* not sure if this is necessary */
          240         return ret;
          241 }