New action: move-cursor-line-start.
[elinks/kon.git] / src / util / memory.c
blob4a067adb6b3456b536aa59f62e10d5b6b403ce56
1 /** Memory allocation manager
2 * @file */
4 #ifndef _GNU_SOURCE
5 #define _GNU_SOURCE /* MREMAP_MAYMOVE */
6 #endif
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
12 #include <stdlib.h>
13 #include <sys/types.h>
14 #ifdef HAVE_MMAP
15 #include <sys/mman.h>
16 #endif
17 #include <unistd.h>
19 #include "elinks.h"
21 #include "util/error.h"
22 #include "util/memory.h"
25 #if !defined(DEBUG_MEMLEAK) && !defined(CONFIG_FASTMEM)
27 static int alloc_try = 0;
29 static int
30 patience(unsigned char *of)
32 ++alloc_try;
33 if (alloc_try < ALLOC_MAXTRIES) {
34 ERROR("Out of memory (%s returned NULL): retry #%d/%d, "
35 "I still exercise my patience and retry tirelessly.",
36 of, alloc_try, ALLOC_MAXTRIES);
37 sleep(ALLOC_DELAY);
38 return alloc_try;
41 #ifdef CRASH_IF_ALLOC_MAXTRIES
42 INTERNAL("Out of memory (%s returned NULL) after %d tries, "
43 "I give up. See ya on the other side.",
44 of, alloc_try);
45 #else
46 ERROR("Out of memory (%s returned NULL) after %d tries, "
47 "I give up and try to continue. Pray for me, please.",
48 of, alloc_try);
49 #endif
50 alloc_try = 0;
51 return 0;
54 void *
55 mem_alloc(size_t size)
57 if (size)
58 do {
59 #ifdef CONFIG_GC
60 void *p = GC_MALLOC(size);
61 #else
62 void *p = malloc(size);
63 #endif
64 if (p) return p;
65 } while (patience("malloc"));
67 return NULL;
70 void *
71 mem_calloc(size_t count, size_t eltsize)
73 if (eltsize && count)
74 do {
75 #ifdef CONFIG_GC
76 void *p = GC_MALLOC(count * eltsize);
77 #else
78 void *p = calloc(count, eltsize);
79 #endif
80 if (p) return p;
81 } while (patience("calloc"));
83 return NULL;
86 void
87 mem_free(void *p)
89 if (!p) {
90 INTERNAL("mem_free(NULL)");
91 return;
93 #ifdef CONFIG_GC
94 p = NULL;
95 #else
96 free(p);
97 #endif
100 void *
101 mem_realloc(void *p, size_t size)
103 if (!p) return mem_alloc(size);
105 if (size)
106 do {
107 #ifdef CONFIG_GC
108 void *p2 = GC_REALLOC(p, size);
109 #else
110 void *p2 = realloc(p, size);
111 #endif
112 if (p2) return p2;
113 } while (patience("realloc"));
114 else
115 mem_free(p);
117 return NULL;
120 #endif
123 /* TODO: Leak detector and the usual protection gear? patience()?
125 * We could just alias mem_mmap_* to mem_debug_* #if DEBUG_MEMLEAK, *WHEN* we are
126 * confident that the mmap() code is really bugless ;-). --pasky */
128 #ifdef HAVE_MMAP
130 static int page_size;
132 /** Round up to a full page.
133 * This tries to prevent useless reallocations, especially since they
134 * are quite expensive in the mremap()-less case. */
135 static size_t
136 round_size(size_t size)
138 #ifdef HAVE_SC_PAGE_SIZE
139 if (!page_size) page_size = sysconf(_SC_PAGE_SIZE);
140 #endif
141 if (page_size <= 0) page_size = 1;
142 return (size / page_size + 1) * page_size;
145 /** Some systems may not have MAP_ANON but MAP_ANONYMOUS instead. */
146 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
147 #define MAP_ANON MAP_ANONYMOUS
148 #endif
150 void *
151 mem_mmap_alloc(size_t size)
153 if (size) {
154 void *p = mmap(NULL, round_size(size), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
156 if (p != MAP_FAILED)
157 return p;
160 return NULL;
163 void
164 mem_mmap_free(void *p, size_t size)
166 if (!p) {
167 INTERNAL("mem_mmap_free(NULL)");
168 return;
171 munmap(p, round_size(size));
174 void *
175 mem_mmap_realloc(void *p, size_t old_size, size_t new_size)
177 if (!p) return mem_mmap_alloc(new_size);
179 if (round_size(old_size) == round_size(new_size))
180 return p;
182 if (new_size) {
183 #ifdef HAVE_MREMAP
184 void *p2 = mremap(p, round_size(old_size), round_size(new_size), MREMAP_MAYMOVE);
186 if (p2 != MAP_FAILED)
187 return p2;
188 #else
189 void *p2 = mem_mmap_alloc(new_size);
191 if (p2) {
192 memcpy(p2, p, MIN(old_size, new_size));
193 mem_mmap_free(p, old_size);
194 return p2;
196 #endif
197 } else {
198 mem_mmap_free(p, old_size);
201 return NULL;
204 #endif