1 /*
2 ** pool.c - memory pool
3 **
4 ** See Copyright Notice in mruby.h
5 */
6
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <string.h>
10 #include <mruby.h>
11
12 /* configuration section */
13 /* allocated memory address should be multiple of POOL_ALIGNMENT */
14 /* or undef it if alignment does not matter */
15 #ifndef POOL_ALIGNMENT
16 #if INTPTR_MAX == INT64_MAX
17 #define POOL_ALIGNMENT 8
18 #else
19 #define POOL_ALIGNMENT 4
20 #endif
21 #endif
22 /* page size of memory pool */
23 #ifndef POOL_PAGE_SIZE
24 #define POOL_PAGE_SIZE 16000
25 #endif
26 /* end of configuration section */
27
28 /* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array
29 * in struct/union" when in C++ mode */
30 #ifdef _MSC_VER
31 #pragma warning(push)
32 #pragma warning(disable : 4200)
33 #endif
34
35 struct mrb_pool_page {
36 struct mrb_pool_page *next;
37 size_t offset;
38 size_t len;
39 void *last;
40 char page[];
41 };
42
43 #ifdef _MSC_VER
44 #pragma warning(pop)
45 #endif
46
47 struct mrb_pool {
48 mrb_state *mrb;
49 struct mrb_pool_page *pages;
50 };
51
52 #undef TEST_POOL
53 #ifdef TEST_POOL
54
55 #define mrb_malloc_simple(m,s) malloc(s)
56 #define mrb_free(m,p) free(p)
57 #endif
58
59 #ifdef POOL_ALIGNMENT
60 # define ALIGN_PADDING(x) ((SIZE_MAX - (x) + 1) & (POOL_ALIGNMENT - 1))
61 #else
62 # define ALIGN_PADDING(x) (0)
63 #endif
64
65 MRB_API mrb_pool*
mrb_pool_open(mrb_state * mrb)66 mrb_pool_open(mrb_state *mrb)
67 {
68 mrb_pool *pool = (mrb_pool *)mrb_malloc_simple(mrb, sizeof(mrb_pool));
69
70 if (pool) {
71 pool->mrb = mrb;
72 pool->pages = NULL;
73 }
74
75 return pool;
76 }
77
78 MRB_API void
mrb_pool_close(mrb_pool * pool)79 mrb_pool_close(mrb_pool *pool)
80 {
81 struct mrb_pool_page *page, *tmp;
82
83 if (!pool) return;
84 page = pool->pages;
85 while (page) {
86 tmp = page;
87 page = page->next;
88 mrb_free(pool->mrb, tmp);
89 }
90 mrb_free(pool->mrb, pool);
91 }
92
93 static struct mrb_pool_page*
page_alloc(mrb_pool * pool,size_t len)94 page_alloc(mrb_pool *pool, size_t len)
95 {
96 struct mrb_pool_page *page;
97
98 if (len < POOL_PAGE_SIZE)
99 len = POOL_PAGE_SIZE;
100 page = (struct mrb_pool_page *)mrb_malloc_simple(pool->mrb, sizeof(struct mrb_pool_page)+len);
101 if (page) {
102 page->offset = 0;
103 page->len = len;
104 }
105
106 return page;
107 }
108
109 MRB_API void*
mrb_pool_alloc(mrb_pool * pool,size_t len)110 mrb_pool_alloc(mrb_pool *pool, size_t len)
111 {
112 struct mrb_pool_page *page;
113 size_t n;
114
115 if (!pool) return NULL;
116 len += ALIGN_PADDING(len);
117 page = pool->pages;
118 while (page) {
119 if (page->offset + len <= page->len) {
120 n = page->offset;
121 page->offset += len;
122 page->last = (char*)page->page+n;
123 return page->last;
124 }
125 page = page->next;
126 }
127 page = page_alloc(pool, len);
128 if (!page) return NULL;
129 page->offset = len;
130 page->next = pool->pages;
131 pool->pages = page;
132
133 page->last = (void*)page->page;
134 return page->last;
135 }
136
137 MRB_API mrb_bool
mrb_pool_can_realloc(mrb_pool * pool,void * p,size_t len)138 mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len)
139 {
140 struct mrb_pool_page *page;
141
142 if (!pool) return FALSE;
143 len += ALIGN_PADDING(len);
144 page = pool->pages;
145 while (page) {
146 if (page->last == p) {
147 size_t beg;
148
149 beg = (char*)p - page->page;
150 if (beg + len > page->len) return FALSE;
151 return TRUE;
152 }
153 page = page->next;
154 }
155 return FALSE;
156 }
157
158 MRB_API void*
mrb_pool_realloc(mrb_pool * pool,void * p,size_t oldlen,size_t newlen)159 mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen)
160 {
161 struct mrb_pool_page *page;
162 void *np;
163
164 if (!pool) return NULL;
165 oldlen += ALIGN_PADDING(oldlen);
166 newlen += ALIGN_PADDING(newlen);
167 page = pool->pages;
168 while (page) {
169 if (page->last == p) {
170 size_t beg;
171
172 beg = (char*)p - page->page;
173 if (beg + oldlen != page->offset) break;
174 if (beg + newlen > page->len) {
175 page->offset = beg;
176 break;
177 }
178 page->offset = beg + newlen;
179 return p;
180 }
181 page = page->next;
182 }
183 np = mrb_pool_alloc(pool, newlen);
184 if (np == NULL) {
185 return NULL;
186 }
187 memcpy(np, p, oldlen);
188 return np;
189 }
190
191 #ifdef TEST_POOL
192 int
main(void)193 main(void)
194 {
195 int i, len = 250;
196 mrb_pool *pool;
197 void *p;
198
199 pool = mrb_pool_open(NULL);
200 p = mrb_pool_alloc(pool, len);
201 for (i=1; i<20; i++) {
202 printf("%p (len=%d) %ud\n", p, len, mrb_pool_can_realloc(pool, p, len*2));
203 p = mrb_pool_realloc(pool, p, len, len*2);
204 len *= 2;
205 }
206 mrb_pool_close(pool);
207 return 0;
208 }
209 #endif
210