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