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