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 }