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 }