]> mj.ucw.cz Git - libucw.git/blob - ucw/mempool.h
Configure: Implemented running of test programs
[libucw.git] / ucw / mempool.h
1 /*
2  *      UCW Library -- Memory Pools
3  *
4  *      (c) 1997--2005 Martin Mares <mj@ucw.cz>
5  *      (c) 2007 Pavel Charvat <pchar@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #ifndef _UCW_POOLS_H
12 #define _UCW_POOLS_H
13
14 /***
15  * [[defs]]
16  * Definitions
17  * -----------
18  ***/
19
20 /**
21  * Memory pool state (see @mp_push(), ...).
22  * You should use this one as an opaque handle only, the insides are internal.
23  **/
24 struct mempool_state {
25   uns free[2];
26   void *last[2];
27   struct mempool_state *next;
28 };
29
30 /**
31  * Memory pool.
32  * You should use this one as an opaque handle only, the insides are internal.
33  **/
34 struct mempool {
35   struct mempool_state state;
36   void *unused, *last_big;
37   uns chunk_size, threshold, idx;
38 };
39
40 struct mempool_stats {                  /** Mempool statistics. See @mp_stats(). **/
41   u64 total_size;                       /* Real allocated size in bytes */
42   uns chain_count[3];                   /* Number of allocated chunks in small/big/unused chains */
43   uns chain_size[3];                    /* Size of allocated chunks in small/big/unused chains */
44 };
45
46 /***
47  * [[basic]]
48  * Basic manipulation
49  * ------------------
50  ***/
51
52 /**
53  * Initialize a given mempool structure.
54  * @chunk_size must be in the interval `[1, UINT_MAX / 2]`.
55  * It will allocate memory by this large chunks and take
56  * memory to satisfy requests from them.
57  **/
58 void mp_init(struct mempool *pool, uns chunk_size);
59
60 /**
61  * Allocate and initialize a new memory pool.
62  * See @mp_init() for @chunk_size limitations.
63  *
64  * The new mempool structure is allocated on the new mempool.
65  **/
66 struct mempool *mp_new(uns chunk_size);
67
68 /**
69  * Cleanup mempool initialized by mp_init or mp_new.
70  * Frees all the memory allocated by this mempool and,
71  * if created by @mp_new(), the @pool itself.
72  **/
73 void mp_delete(struct mempool *pool);
74
75 /**
76  * Frees all data on a memory pool, but leaves it working.
77  * It can keep some of the chunks allocated to serve
78  * further allocation requests. Leaves the @pool alive,
79  * even if it was created with @mp_new().
80  **/
81 void mp_flush(struct mempool *pool);
82
83 /**
84  * Compute some statistics for debug purposes.
85  * See the definition of the <<struct_mempool_stats,mempool_stats structure>>.
86  **/
87 void mp_stats(struct mempool *pool, struct mempool_stats *stats);
88 u64 mp_total_size(struct mempool *pool);        /** How many bytes were allocated by the pool. **/
89
90
91 /***
92  * [[alloc]]
93  * Allocation routines
94  * -------------------
95  ***/
96
97 /* For internal use only, do not call directly */
98 void *mp_alloc_internal(struct mempool *pool, uns size) LIKE_MALLOC;
99
100 /**
101  * The function allocates new @size bytes on a given memory pool.
102  * If the @size is zero, the resulting pointer is undefined,
103  * but it may be safely reallocated or used as the parameter
104  * to other functions below.
105  *
106  * The resulting pointer is always aligned to a multiple of
107  * `CPU_STRUCT_ALIGN` bytes and this condition remains true also
108  * after future reallocations.
109  **/
110 void *mp_alloc(struct mempool *pool, uns size);
111
112 /**
113  * The same as @mp_alloc(), but the result may be unaligned.
114  **/
115 void *mp_alloc_noalign(struct mempool *pool, uns size);
116
117 /**
118  * The same as @mp_alloc(), but fills the newly allocated memory with zeroes.
119  **/
120 void *mp_alloc_zero(struct mempool *pool, uns size);
121
122 /**
123  * Inlined version of @mp_alloc().
124  **/
125 static inline void *mp_alloc_fast(struct mempool *pool, uns size)
126 {
127   uns avail = pool->state.free[0] & ~(CPU_STRUCT_ALIGN - 1);
128   if (size <= avail)
129     {
130       pool->state.free[0] = avail - size;
131       return (byte *)pool->state.last[0] - avail;
132     }
133   else
134     return mp_alloc_internal(pool, size);
135 }
136
137 /**
138  * Inlined version of @mp_alloc_noalign().
139  **/
140 static inline void *mp_alloc_fast_noalign(struct mempool *pool, uns size)
141 {
142   if (size <= pool->state.free[0])
143     {
144       void *ptr = (byte *)pool->state.last[0] - pool->state.free[0];
145       pool->state.free[0] -= size;
146       return ptr;
147     }
148   else
149     return mp_alloc_internal(pool, size);
150 }
151
152 /***
153  * [[gbuf]]
154  * Growing buffers
155  * ---------------
156  *
157  * You do not need to know, how a buffer will need to be large,
158  * you can grow it incrementally to needed size. You can grow only
159  * one buffer at a time on a given mempool.
160  *
161  * Similar functionality is provided by <<growbuf:,growing buffes>> module.
162  ***/
163
164 /* For internal use only, do not call directly */
165 void *mp_start_internal(struct mempool *pool, uns size) LIKE_MALLOC;
166 void *mp_grow_internal(struct mempool *pool, uns size);
167 void *mp_spread_internal(struct mempool *pool, void *p, uns size);
168
169 static inline uns
170 mp_idx(struct mempool *pool, void *ptr)
171 {
172   return ptr == pool->last_big;
173 }
174
175 /**
176  * Open a new growing buffer (at least @size bytes long).
177  * If the @size is zero, the resulting pointer is undefined,
178  * but it may be safely reallocated or used as the parameter
179  * to other functions below.
180  *
181  * The resulting pointer is always aligned to a multiple of
182  * `CPU_STRUCT_ALIGN` bytes and this condition remains true also
183  * after future reallocations. There is an unaligned version as well.
184  *
185  * Keep in mind that you can't make any other pool allocations
186  * before you "close" the growing buffer with @mp_end().
187  */
188 void *mp_start(struct mempool *pool, uns size);
189 void *mp_start_noalign(struct mempool *pool, uns size);
190
191 /**
192  * Inlined version of @mp_start().
193  **/
194 static inline void *mp_start_fast(struct mempool *pool, uns size)
195 {
196   uns avail = pool->state.free[0] & ~(CPU_STRUCT_ALIGN - 1);
197   if (size <= avail)
198     {
199       pool->idx = 0;
200       pool->state.free[0] = avail;
201       return (byte *)pool->state.last[0] - avail;
202     }
203   else
204     return mp_start_internal(pool, size);
205 }
206
207 /**
208  * Inlined version of @mp_start_noalign().
209  **/
210 static inline void *mp_start_fast_noalign(struct mempool *pool, uns size)
211 {
212   if (size <= pool->state.free[0])
213     {
214       pool->idx = 0;
215       return (byte *)pool->state.last[0] - pool->state.free[0];
216     }
217   else
218     return mp_start_internal(pool, size);
219 }
220
221 /**
222  * Return start pointer of the growing buffer allocated by latest @mp_start() or a similar function.
223  **/
224 static inline void *mp_ptr(struct mempool *pool)
225 {
226   return (byte *)pool->state.last[pool->idx] - pool->state.free[pool->idx];
227 }
228
229 /**
230  * Return the number of bytes available for extending the growing buffer.
231  * (Before a reallocation will be needed).
232  **/
233 static inline uns mp_avail(struct mempool *pool)
234 {
235   return pool->state.free[pool->idx];
236 }
237
238 /**
239  * Grow the buffer allocated by @mp_start() to be at least @size bytes long
240  * (@size may be less than @mp_avail(), even zero). Reallocated buffer may
241  * change its starting position. The content will be unchanged to the minimum
242  * of the old and new sizes; newly allocated memory will be uninitialized.
243  * Multiple calls to mp_grow() have amortized linear cost wrt. the maximum value of @size. */
244 static inline void *mp_grow(struct mempool *pool, uns size)
245 {
246   return (size <= mp_avail(pool)) ? mp_ptr(pool) : mp_grow_internal(pool, size);
247 }
248
249 /**
250  * Grow the buffer by at least one byte -- equivalent to <<mp_grow(),`mp_grow`>>`(@pool, @mp_avail(pool) + 1)`.
251  **/
252 static inline void *mp_expand(struct mempool *pool)
253 {
254   return mp_grow_internal(pool, mp_avail(pool) + 1);
255 }
256
257 /**
258  * Ensure that there is at least @size bytes free after @p,
259  * if not, reallocate and adjust @p.
260  **/
261 static inline void *mp_spread(struct mempool *pool, void *p, uns size)
262 {
263   return (((uns)((byte *)pool->state.last[pool->idx] - (byte *)p) >= size) ? p : mp_spread_internal(pool, p, size));
264 }
265
266 /**
267  * Close the growing buffer. The @end must point just behind the data, you want to keep
268  * allocated (so it can be in the interval `[@mp_ptr(@pool), @mp_ptr(@pool) + @mp_avail(@pool)]`).
269  * Returns a pointer to the beginning of the just closed block.
270  **/
271 static inline void *mp_end(struct mempool *pool, void *end)
272 {
273   void *p = mp_ptr(pool);
274   pool->state.free[pool->idx] = (byte *)pool->state.last[pool->idx] - (byte *)end;
275   return p;
276 }
277
278 /**
279  * Return size in bytes of the last allocated memory block (with @mp_alloc() or @mp_end()).
280  **/
281 static inline uns mp_size(struct mempool *pool, void *ptr)
282 {
283   uns idx = mp_idx(pool, ptr);
284   return ((byte *)pool->state.last[idx] - (byte *)ptr) - pool->state.free[idx];
285 }
286
287 /**
288  * Open the last memory block (allocated with @mp_alloc() or @mp_end())
289  * for growing and return its size in bytes. The contents and the start pointer
290  * remain unchanged. Do not forget to call @mp_end() to close it.
291  **/
292 uns mp_open(struct mempool *pool, void *ptr);
293
294 /**
295  * Inlined version of mp_open().
296  **/
297 static inline uns mp_open_fast(struct mempool *pool, void *ptr)
298 {
299   pool->idx = mp_idx(pool, ptr);
300   uns size = ((byte *)pool->state.last[pool->idx] - (byte *)ptr) - pool->state.free[pool->idx];
301   pool->state.free[pool->idx] += size;
302   return size;
303 }
304
305 /**
306  * Reallocate the last memory block (allocated with @mp_alloc() or @mp_end())
307  * to the new @size. Behavior is similar to @mp_grow(), but the resulting
308  * block is closed.
309  **/
310 void *mp_realloc(struct mempool *pool, void *ptr, uns size);
311
312 /**
313  * The same as @mp_realloc(), but fills the additional bytes (if any) with zeroes.
314  **/
315 void *mp_realloc_zero(struct mempool *pool, void *ptr, uns size);
316
317 /**
318  * Inlined version of mp_realloc().
319  **/
320 static inline void *mp_realloc_fast(struct mempool *pool, void *ptr, uns size)
321 {
322   mp_open_fast(pool, ptr);
323   ptr = mp_grow(pool, size);
324   mp_end(pool, (byte *)ptr + size);
325   return ptr;
326 }
327
328 /***
329  * [[store]]
330  * Storing and restoring state
331  * ---------------------------
332  *
333  * Mempools can remember history of what was allocated and return back
334  * in time.
335  ***/
336
337 /**
338  * Save the current state of a memory pool.
339  * Do not call this function with an opened growing buffer. 
340  **/
341 static inline void mp_save(struct mempool *pool, struct mempool_state *state)
342 {
343   *state = pool->state;
344   pool->state.next = state;
345 }
346
347 /**
348  * Save the current state to a newly allocated mempool_state structure.
349  * Do not call this function with an opened growing buffer.
350  **/
351 struct mempool_state *mp_push(struct mempool *pool);
352
353 /**
354  * Restore the state saved by @mp_save() or @mp_push() and free all
355  * data allocated after that point (including the state structure itself).
356  * You can't reallocate the last memory block from the saved state.
357  **/
358 void mp_restore(struct mempool *pool, struct mempool_state *state);
359
360 /**
361  * Inlined version of @mp_restore().
362  **/
363 static inline void mp_restore_fast(struct mempool *pool, struct mempool_state *state)
364 {
365   if (pool->state.last[0] != state->last[0] || pool->state.last[1] != state->last[1])
366     mp_restore(pool, state);
367   else
368     {
369       pool->state = *state;
370       pool->last_big = &pool->last_big;
371     }
372 }
373
374 /**
375  * Restore the state saved by the last call to @mp_push().
376  * @mp_pop() and @mp_push() works as a stack so you can push more states safely.
377  **/
378 void mp_pop(struct mempool *pool);
379
380
381 /***
382  * [[string]]
383  * String operations
384  * -----------------
385  ***/
386
387 char *mp_strdup(struct mempool *, const char *) LIKE_MALLOC;            /** Makes a copy of a string on a mempool. **/
388 void *mp_memdup(struct mempool *, const void *, uns) LIKE_MALLOC;       /** Makes a copy of a memory block on a mempool. **/
389 /**
390  * Concatenates all passed strings. The last parameter must be NULL.
391  * This will concatenate two strings:
392  *
393  *   char *message = mp_multicat(pool, "hello ", "world", NULL);
394  **/
395 char *mp_multicat(struct mempool *, ...) LIKE_MALLOC SENTINEL_CHECK;
396 /**
397  * Concatenates two strings and stores result on @mp.
398  */
399 static inline char *LIKE_MALLOC mp_strcat(struct mempool *mp, const char *x, const char *y)
400 {
401   return mp_multicat(mp, x, y, NULL);
402 }
403 /**
404  * Join strings and place @sep between each two neighboring.
405  * @p is the mempool to provide memory, @a is array of strings and @n
406  * tells how many there is of them.
407  **/
408 char *mp_strjoin(struct mempool *p, char **a, uns n, uns sep) LIKE_MALLOC;
409
410
411 /***
412  * [[format]]
413  * Formatted output
414  * ---------------
415  ***/
416
417 /**
418  * printf() into a in-memory string, allocated on the memory pool.
419  **/
420 char *mp_printf(struct mempool *mp, const char *fmt, ...) FORMAT_CHECK(printf,2,3) LIKE_MALLOC;
421 /**
422  * Like @mp_printf(), but uses `va_list` for parameters.
423  **/
424 char *mp_vprintf(struct mempool *mp, const char *fmt, va_list args) LIKE_MALLOC;
425 /**
426  * Like @mp_printf(), but it appends the data at the end of string
427  * pointed to by @ptr. The string is @mp_open()ed, so you have to
428  * provide something that can be.
429  *
430  * Returns pointer to the beginning of the string (the pointer may have
431  * changed due to reallocation).
432  **/
433 char *mp_printf_append(struct mempool *mp, char *ptr, const char *fmt, ...) FORMAT_CHECK(printf,3,4);
434 /**
435  * Like @mp_printf_append(), but uses `va_list` for parameters.
436  **/
437 char *mp_vprintf_append(struct mempool *mp, char *ptr, const char *fmt, va_list args);
438
439 #endif