]> mj.ucw.cz Git - libucw.git/blob - ucw/mempool.h
f566b79cd2a5fb1e4cf3e285b23af81b01ba8532
[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 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 = 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
162 /* For internal use only, do not call directly */
163 void *mp_start_internal(struct mempool *pool, uns size) LIKE_MALLOC;
164 void *mp_grow_internal(struct mempool *pool, uns size);
165 void *mp_spread_internal(struct mempool *pool, void *p, uns size);
166
167 static inline uns
168 mp_idx(struct mempool *pool, void *ptr)
169 {
170   return ptr == pool->last_big;
171 }
172
173 /**
174  * Open a new growing buffer (at least @size bytes long).
175  * If the @size is zero, the resulting pointer is undefined,
176  * but it may be safely reallocated or used as the parameter
177  * to other functions below.
178  *
179  * The resulting pointer is always aligned to a multiple of
180  * `CPU_STRUCT_ALIGN` bytes and this condition remains true also
181  * after future reallocations. There is an unaligned version as well.
182  *
183  * Keep in mind that you can't make any other pool allocations
184  * before you "close" the growing buffer with @mp_end().
185  */
186 void *mp_start(struct mempool *pool, uns size);
187 void *mp_start_noalign(struct mempool *pool, uns size);
188
189 /**
190  * Inlined version of @mp_start().
191  **/
192 static inline void *mp_start_fast(struct mempool *pool, uns size)
193 {
194   uns avail = pool->state.free[0] & ~(CPU_STRUCT_ALIGN - 1);
195   if (size <= avail)
196     {
197       pool->idx = 0;
198       pool->state.free[0] = avail;
199       return pool->state.last[0] - avail;
200     }
201   else
202     return mp_start_internal(pool, size);
203 }
204
205 /**
206  * Inlined version of @mp_start_noalign().
207  **/
208 static inline void *mp_start_fast_noalign(struct mempool *pool, uns size)
209 {
210   if (size <= pool->state.free[0])
211     {
212       pool->idx = 0;
213       return pool->state.last[0] - pool->state.free[0];
214     }
215   else
216     return mp_start_internal(pool, size);
217 }
218
219 /**
220  * Return start pointer of the growing buffer allocated by latest @mp_start() or a similar function.
221  **/
222 static inline void *mp_ptr(struct mempool *pool)
223 {
224   return pool->state.last[pool->idx] - pool->state.free[pool->idx];
225 }
226
227 /**
228  * Return the number of bytes available for extending the growing buffer.
229  * (Before a reallocation will be needed).
230  **/
231 static inline uns mp_avail(struct mempool *pool)
232 {
233   return pool->state.free[pool->idx];
234 }
235
236 /**
237  * Grow the buffer allocated by @mp_start() to be at least @size bytes long
238  * (@size may be less than @mp_avail(), even zero). Reallocated buffer may
239  * change its starting position. The content will be unchanged to the minimum
240  * of the old and new sizes; newly allocated memory will be uninitialized.
241  * Multiple calls to mp_grow() have amortized linear cost wrt. the maximum value of @size. */
242 static inline void *mp_grow(struct mempool *pool, uns size)
243 {
244   return (size <= mp_avail(pool)) ? mp_ptr(pool) : mp_grow_internal(pool, size);
245 }
246
247 /**
248  * Grow the buffer by at least one byte -- equivalent to <<mp_grow(),`mp_grow`>>`(@pool, @mp_avail(pool) + 1)`.
249  **/
250 static inline void *mp_expand(struct mempool *pool)
251 {
252   return mp_grow_internal(pool, mp_avail(pool) + 1);
253 }
254
255 /**
256  * Ensure that there is at least @size bytes free after @p,
257  * if not, reallocate and adjust @p.
258  **/
259 static inline void *mp_spread(struct mempool *pool, void *p, uns size)
260 {
261   return (((uns)(pool->state.last[pool->idx] - p) >= size) ? p : mp_spread_internal(pool, p, size));
262 }
263
264 /**
265  * Close the growing buffer. The @end must point just behind the data, you want to keep
266  * allocated (so it can be in the interval `[@mp_ptr(@pool), @mp_ptr(@pool) + @mp_avail(@pool)]`).
267  * Returns a pointer to the beginning of the just closed block.
268  **/
269 static inline void *mp_end(struct mempool *pool, void *end)
270 {
271   void *p = mp_ptr(pool);
272   pool->state.free[pool->idx] = pool->state.last[pool->idx] - end;
273   return p;
274 }
275
276 /**
277  * Return size in bytes of the last allocated memory block (with @mp_alloc() or @mp_end()).
278  **/
279 static inline uns mp_size(struct mempool *pool, void *ptr)
280 {
281   uns idx = mp_idx(pool, ptr);
282   return pool->state.last[idx] - ptr - pool->state.free[idx];
283 }
284
285 /**
286  * Open the last memory block (allocated with @mp_alloc() or @mp_end())
287  * for growing and return its size in bytes. The contents and the start pointer
288  * remain unchanged. Do not forget to call @mp_end() to close it.
289  **/
290 uns mp_open(struct mempool *pool, void *ptr);
291
292 /**
293  * Inlined version of mp_open().
294  **/
295 static inline uns mp_open_fast(struct mempool *pool, void *ptr)
296 {
297   pool->idx = mp_idx(pool, ptr);
298   uns size = pool->state.last[pool->idx] - ptr - pool->state.free[pool->idx];
299   pool->state.free[pool->idx] += size;
300   return size;
301 }
302
303 /**
304  * Reallocate the last memory block (allocated with @mp_alloc() or @mp_end())
305  * to the new @size. Behavior is similar to @mp_grow(), but the resulting
306  * block is closed.
307  **/
308 void *mp_realloc(struct mempool *pool, void *ptr, uns size);
309
310 /**
311  * The same as @mp_realloc(), but fills the additional bytes (if any) with zeroes.
312  **/
313 void *mp_realloc_zero(struct mempool *pool, void *ptr, uns size);
314
315 /**
316  * Inlined version of mp_realloc().
317  **/
318 static inline void *mp_realloc_fast(struct mempool *pool, void *ptr, uns size)
319 {
320   mp_open_fast(pool, ptr);
321   ptr = mp_grow(pool, size);
322   mp_end(pool, ptr + size);
323   return ptr;
324 }
325
326 /***
327  * [[store]]
328  * Storing and restoring state
329  * ---------------------------
330  *
331  * Mempools can remember history of what was allocated and return back
332  * in time.
333  ***/
334
335 /**
336  * Save the current state of a memory pool.
337  * Do not call this function with an opened growing buffer. 
338  **/
339 static inline void mp_save(struct mempool *pool, struct mempool_state *state)
340 {
341   *state = pool->state;
342   pool->state.next = state;
343 }
344
345 /**
346  * Save the current state to a newly allocated mempool_state structure.
347  * Do not call this function with an opened growing buffer.
348  **/
349 struct mempool_state *mp_push(struct mempool *pool);
350
351 /**
352  * Restore the state saved by @mp_save() or @mp_push() and free all
353  * data allocated after that point (including the state structure itself).
354  * You can't reallocate the last memory block from the saved state.
355  **/
356 void mp_restore(struct mempool *pool, struct mempool_state *state);
357
358 /**
359  * Inlined version of @mp_restore().
360  **/
361 static inline void mp_restore_fast(struct mempool *pool, struct mempool_state *state)
362 {
363   if (pool->state.last[0] != state->last[0] || pool->state.last[1] != state->last[1])
364     mp_restore(pool, state);
365   else
366     {
367       pool->state = *state;
368       pool->last_big = &pool->last_big;
369     }
370 }
371
372 /**
373  * Restore the state saved by the last call to @mp_push().
374  * @mp_pop() and @mp_push() works as a stack so you can push more states safely.
375  **/
376 void mp_pop(struct mempool *pool);
377
378
379 /***
380  * [[string]]
381  * String operations
382  * -----------------
383  ***/
384
385 char *mp_strdup(struct mempool *, const char *) LIKE_MALLOC;            /** Makes a copy of a string on a mempool. **/
386 void *mp_memdup(struct mempool *, const void *, uns) LIKE_MALLOC;       /** Makes a copy of a memory block on a mempool. **/
387 /**
388  * Concatenates all passed strings. The last parameter must be NULL.
389  * This will concatenate two strings:
390  *
391  *   char *message = mp_multicat(pool, "hello ", "world", NULL);
392  **/
393 char *mp_multicat(struct mempool *, ...) LIKE_MALLOC SENTINEL_CHECK;
394 /**
395  * Concatenates two strings and stores result on @mp.
396  */
397 static inline char *LIKE_MALLOC mp_strcat(struct mempool *mp, const char *x, const char *y)
398 {
399   return mp_multicat(mp, x, y, NULL);
400 }
401 /**
402  * Join strings and place @sep between each two neighboring.
403  * @p is the mempool to provide memory, @a is array of strings and @n
404  * tells how many there is of them.
405  **/
406 char *mp_strjoin(struct mempool *p, char **a, uns n, uns sep) LIKE_MALLOC;
407
408
409 /***
410  * [[format]]
411  * Formatted output
412  * ---------------
413  ***/
414
415 /**
416  * printf() into a in-memory string, allocated on the memory pool.
417  **/
418 char *mp_printf(struct mempool *mp, const char *fmt, ...) FORMAT_CHECK(printf,2,3) LIKE_MALLOC;
419 /**
420  * Like @mp_printf(), but uses `va_list` for parameters.
421  **/
422 char *mp_vprintf(struct mempool *mp, const char *fmt, va_list args) LIKE_MALLOC;
423 /**
424  * Like @mp_printf(), but it appends the data at the end of string
425  * pointed to by @ptr. The string is @mp_open()ed, so you have to
426  * provide something that can be.
427  *
428  * Returns pointer to the beginning of the string (the pointer may have
429  * changed due to reallocation).
430  **/
431 char *mp_printf_append(struct mempool *mp, char *ptr, const char *fmt, ...) FORMAT_CHECK(printf,3,4);
432 /**
433  * Like @mp_printf_append(), but uses `va_list` for parameters.
434  **/
435 char *mp_vprintf_append(struct mempool *mp, char *ptr, const char *fmt, va_list args);
436
437 #endif