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