Missed one void-pointer-arithmetic case.
[rsync/rsync.git] / lib / pool_alloc.c
1 #include "rsync.h"
2
3 #define POOL_DEF_EXTENT (32 * 1024)
4
5 struct alloc_pool
6 {
7         size_t                  size;           /* extent size          */
8         size_t                  quantum;        /* allocation quantum   */
9         struct pool_extent      *live;          /* current extent for
10                                                  * allocations          */
11         struct pool_extent      *free;          /* unfreed extent list  */
12         void                    (*bomb)();
13                                                 /* function to call if
14                                                  * malloc fails         */
15         int                     flags;
16
17         /* statistical data */
18         unsigned long           e_created;      /* extents created      */
19         unsigned long           e_freed;        /* extents detroyed     */
20         uint64                  n_allocated;    /* calls to alloc       */
21         uint64                  n_freed;        /* calls to free        */
22         uint64                  b_allocated;    /* cum. bytes allocated */
23         uint64                  b_freed;        /* cum. bytes freed     */
24 };
25
26 struct pool_extent
27 {
28         void                    *start;         /* starting address     */
29         size_t                  free;           /* free bytecount       */
30         size_t                  bound;          /* bytes bound by padding,
31                                                  * overhead and freed   */
32         struct pool_extent      *next;
33 };
34
35 struct align_test {
36     void *foo;
37     uint64 bar;
38 };
39
40 #define MINALIGN        offsetof(struct align_test, bar)
41
42 /* Temporarily cast a void* var into a char* var when adding an offset (to
43  * keep some compilers from complaining about the pointer arithmetic). */
44 #define PTR_ADD(b,o)    ( (void*) ((char*)(b) + (o)) )
45
46 alloc_pool_t
47 pool_create(size_t size, size_t quantum,
48     void (*bomb)(char *), int flags)
49 {
50         struct alloc_pool       *pool;
51
52         if (!(pool = (struct alloc_pool*) malloc(sizeof (struct alloc_pool))))
53                 return pool;
54         memset(pool, 0, sizeof (struct alloc_pool));
55
56         pool->size = size       /* round extent size to min alignment reqs */
57             ? (size + MINALIGN - 1) & ~(MINALIGN - 1)
58             : POOL_DEF_EXTENT;
59         if (pool->flags & POOL_INTERN)
60         {
61                 pool->size -= sizeof (struct pool_extent);
62                 flags |= POOL_APPEND;
63         }
64         pool->quantum = quantum ? quantum : MINALIGN;
65         pool->bomb = bomb;
66         pool->flags = flags;
67
68         return pool;
69 }
70
71 void
72 pool_destroy(alloc_pool_t p)
73 {
74         struct alloc_pool *pool = (struct alloc_pool *) p;
75         struct pool_extent      *cur, *next;
76
77         if (!pool)
78                 return;
79
80         if (pool->live)
81         {
82                 cur = pool->live;
83                 free(cur->start);
84                 if (!(pool->flags & POOL_APPEND))
85                         free(cur);
86         }
87         for (cur = pool->free; cur; cur = next)
88         {
89                 next = cur->next;
90                 free(cur->start);
91                 if (!(pool->flags & POOL_APPEND))
92                         free(cur);
93         }
94         free(pool);
95 }
96
97 void *
98 pool_alloc(alloc_pool_t p, size_t len, char *bomb)
99 {
100         struct alloc_pool *pool = (struct alloc_pool *) p;
101         if (!pool)
102                 return NULL;
103
104         if (!len)
105                 len = pool->quantum;
106         else if (pool->quantum > 1 && len % pool->quantum)
107                 len += pool->quantum - len % pool->quantum;
108
109         if (len > pool->size)
110                 goto bomb;
111
112         if (!pool->live || len > pool->live->free)
113         {
114                 void    *start;
115                 size_t  free;
116                 size_t  bound;
117                 size_t  sqew;
118                 size_t  asize;
119
120                 if (pool->live)
121                 {
122                         pool->live->next = pool->free;
123                         pool->free = pool->live;
124                 }
125
126                 free = pool->size;
127                 bound = 0;
128
129                 asize = pool->size;
130                 if (pool->flags & POOL_APPEND)
131                         asize += sizeof (struct pool_extent);
132
133                 if (!(start = (void *) malloc(asize)))
134                         goto bomb;
135
136                 if (pool->flags & POOL_CLEAR)
137                         memset(start, 0, pool->size);
138
139                 if (pool->flags & POOL_APPEND)
140                 {
141                         pool->live = PTR_ADD(start, free);
142                 }
143                 else if (!(pool->live = (struct pool_extent *) malloc(sizeof (struct pool_extent))))
144                 {
145                         goto bomb;
146                 }
147                 if (pool->flags & POOL_QALIGN && pool->quantum > 1
148                     && (sqew = (size_t)PTR_ADD(start, free) % pool->quantum))
149                 {
150                         bound  += sqew;
151                         free -= sqew;
152                 }
153                 pool->live->start = start;
154                 pool->live->free = free;
155                 pool->live->bound = bound;
156                 pool->live->next = NULL;
157
158                 pool->e_created++;
159         }
160
161         pool->n_allocated++;
162         pool->b_allocated += len;
163
164         pool->live->free -= len;
165
166         return PTR_ADD(pool->live->start, pool->live->free);
167
168 bomb:
169         if (pool->bomb)
170                 (*pool->bomb)(bomb);
171         return NULL;
172 }
173
174 void
175 pool_free(alloc_pool_t p, size_t len, void *addr)
176 {
177         struct alloc_pool *pool = (struct alloc_pool *) p;
178         struct pool_extent      *cur;
179         struct pool_extent      *prev;
180
181         if (!pool)
182                 return;
183
184         if (!len)
185                 len = pool->quantum;
186         else if (pool->quantum > 1 && len % pool->quantum)
187                 len += pool->quantum - len % pool->quantum;
188
189         if (!addr && pool->live)
190         {
191                 pool->live->next = pool->free;
192                 pool->free = pool->live;
193                 pool->live = NULL;
194                 return;
195         }
196         pool->n_freed++;
197         pool->b_freed += len;
198
199         cur = pool->live;
200         if (cur
201             && addr >= cur->start
202             && addr < PTR_ADD(cur->start, pool->size))
203         {
204                 if (addr == PTR_ADD(cur->start, cur->free))
205                 {
206                         if (pool->flags & POOL_CLEAR)
207                                 memset(addr, 0, len);
208                         pool->b_freed += len;
209                 } else {
210                         cur->bound += len;
211                 }
212                 if (cur->free + cur->bound >= pool->size)
213                 {
214                         size_t sqew;
215
216                         cur->free = pool->size;
217                         cur->bound = 0;
218                         if (pool->flags & POOL_QALIGN && pool->quantum > 1
219                             && (sqew = (size_t)PTR_ADD(cur->start, cur->free) % pool->quantum))
220                         {
221                                 cur->bound += sqew;
222                                 cur->free -= sqew;
223                         }
224                 }
225                 return;
226         }
227         for (prev = NULL, cur = pool->free; cur; prev = cur, cur = cur->next)
228         {
229                 if (addr >= cur->start
230                     && addr < PTR_ADD(cur->start, pool->size))
231                         break;
232         }
233         if (!cur)
234                 return;
235
236         if (prev)
237         {
238                 prev->next = cur->next;
239                 cur->next = pool->free;
240                 pool->free = cur;
241         }
242         cur->bound += len;
243
244         if (cur->free + cur->bound >= pool->size)
245         {
246                 pool->free = cur->next;
247
248                 free(cur->start);
249                 if (!(pool->flags & POOL_APPEND))
250                         free(cur);
251                 pool->e_freed++;
252         }
253         return;
254 }
255
256 #define FDPRINT(label, value) \
257         snprintf(buf, BUFSIZ, label, value), \
258         write(fd, buf, strlen(buf));
259
260 #define FDEXTSTAT(ext) \
261         snprintf(buf, BUFSIZ, "  %12ld  %5ld\n", \
262                 (long) ext->free, \
263                 (long) ext->bound), \
264         write(fd, buf, strlen(buf))
265
266 void
267 pool_stats(alloc_pool_t p, int fd, int summarize)
268 {
269         struct alloc_pool *pool = (struct alloc_pool *) p;
270         struct pool_extent      *cur;
271         char buf[BUFSIZ];
272
273         if (!pool)
274                 return;
275
276         FDPRINT("  Extent size:       %12ld\n", (long)  pool->size);
277         FDPRINT("  Alloc quantum:     %12ld\n", (long)  pool->quantum);
278         FDPRINT("  Extents created:   %12ld\n",         pool->e_created);
279         FDPRINT("  Extents freed:     %12ld\n",         pool->e_freed);
280         FDPRINT("  Alloc count:       %12.0f\n", (double) pool->n_allocated);
281         FDPRINT("  Free Count:        %12.0f\n", (double) pool->n_freed);
282         FDPRINT("  Alloc bytes:       %12.0f\n", (double) pool->b_allocated);
283         FDPRINT("  Free bytes:        %12.0f\n", (double) pool->b_freed);
284
285         if (summarize)
286                 return;
287
288         if (!pool->live && !pool->free)
289                 return;
290
291         write(fd, "\n", 1);
292
293         if (pool->live)
294         {
295                 FDEXTSTAT(pool->live);
296         }
297         strcpy(buf, "   FREE    BOUND\n");
298         write(fd, buf, strlen(buf));
299
300         for (cur = pool->free; cur; cur = cur->next)
301         {
302                 FDEXTSTAT(cur);
303         }
304 }