data-stack.h revision 33b0119d4effb14cd0f1bdd3ad5f2954e3b1e63e
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#ifndef DATA_STACK_H
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define DATA_STACK_H
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen/* Data stack makes it very easy to implement functions returning dynamic data
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen without having to worry much about memory management like freeing the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen result or having large enough buffers for the result.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_ prefix was chosen to describe functions allocating memory from data
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen stack. "t" meaning temporary.
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen Advantages over control stack and alloca():
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen - Functions can return a value allocated from data stack
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen - We can portably specify how much data we want to allocate at runtime
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen Advantages over malloc():
7d7b5c98f086ffa8ac9c90f21db17748ca607202Timo Sirainen - FAST, most of the time allocating memory means only updating a couple of
7d7b5c98f086ffa8ac9c90f21db17748ca607202Timo Sirainen pointers and integers. Freeing the memory all at once also is a fast
7d7b5c98f086ffa8ac9c90f21db17748ca607202Timo Sirainen operation.
7d7b5c98f086ffa8ac9c90f21db17748ca607202Timo Sirainen - No need to free() each allocation resulting in prettier code
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen - No memory leaks
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen - No memory fragmentation
e627cdc5ef30d87959f9510832427e33a2f1d84aTimo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen Disadvantages:
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen - Allocating memory inside loops can accidentally allocate a lot of memory
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if the loops are long and you forgot to place t_push() and t_pop() there.
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen - t_malloc()ed data could be accidentally stored into permanent location
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen and accessed after it's already been freed. const'ing the return values
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen helps for most uses though (see the t_malloc() description).
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen - Debugging invalid memory usage may be difficult using existing tools,
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen although compiling with DEBUG enabled helps finding simple buffer
ff7257145f317d6ca44a9402427bb74c34b999a9Timo Sirainen overflows.
ff7257145f317d6ca44a9402427bb74c34b999a9Timo Sirainen*/
ff7257145f317d6ca44a9402427bb74c34b999a9Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenextern unsigned int data_stack_frame;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen/* All t_..() allocations between t_push*() and t_pop() are freed after t_pop()
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen is called. Returns the current stack frame number, which can be used
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen to detect missing t_pop() calls:
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen x = t_push(__func__); .. if (t_pop() != x) abort();
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen In DEBUG mode, t_push_named() makes a temporary allocation for the name,
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen but is safe to call in a loop as it performs the allocation within its own
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen frame. However, you should always prefer to use T_BEGIN { ... } T_END below.
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen*/
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenunsigned int t_push(const char *marker) ATTR_HOT;
03739a8eaad2d8b34b9d87dbbe5b13c5d5dfa11aTimo Sirainenunsigned int t_push_named(const char *format, ...) ATTR_HOT ATTR_FORMAT(1, 2);
03739a8eaad2d8b34b9d87dbbe5b13c5d5dfa11aTimo Sirainenunsigned int t_pop(void) ATTR_HOT;
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen/* Simplifies the if (t_pop() != x) check by comparing it internally and
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen panicking if it doesn't match. */
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainenvoid t_pop_check(unsigned int *id) ATTR_HOT;
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen/* Usage: T_BEGIN { code } T_END */
ff7257145f317d6ca44a9402427bb74c34b999a9Timo Sirainen#ifndef DEBUG
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define T_BEGIN \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen STMT_START { unsigned int _data_stack_cur_id = t_push(NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#elif defined (__GNUC__) && !defined (__STRICT_ANSI__)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen#define T_BEGIN \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen STMT_START { unsigned int _data_stack_cur_id = t_push(__FUNCTION__);
3b80595fcf2001cf7b2fcc6290823e38f4a142fcTimo Sirainen#else
3b80595fcf2001cf7b2fcc6290823e38f4a142fcTimo Sirainen#define T_CAT2(a,b) (a ":" #b)
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen#define T_BEGIN \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen STMT_START { unsigned int _data_stack_cur_id = t_push(T_CAT2(__FILE__,__LINE__));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#endif
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define T_END \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_pop_check(&_data_stack_cur_id); } STMT_END
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* WARNING: Be careful when using these functions, it's too easy to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen accidentally save the returned value somewhere permanently.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen You probably should never use these functions directly, rather
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen create functions that return 'const xxx*' types and use t_malloc()
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen internally in them. This is a lot safer, since usually compiler
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen warns if you try to place them in xxx*. See strfuncs.c for examples.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_malloc() calls never fail. If there's not enough memory left,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_panic() will be called. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid *t_malloc(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid *t_malloc0(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Try growing allocated memory. Returns TRUE if successful. Works only
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for last allocated memory in current stack frame. */
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenbool t_try_realloc(void *mem, size_t size);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen/* Returns the number of bytes available in data stack without allocating
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen more memory. */
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainensize_t t_get_bytes_available(void) ATTR_PURE;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen#define t_new(type, count) \
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen ((type *) t_malloc0(sizeof(type) * (count)))
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen/* Returns pointer to a temporary buffer you can use. The buffer will be
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen invalid as soon as next t_malloc() is called!
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen If you wish to grow the buffer, you must give the full wanted size
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen in the size parameter. If return value doesn't point to the same value
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen as last time, you need to memcpy() data from the old buffer to the
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen new one (or do some other trickery). See t_buffer_reget(). */
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen#define t_buffer_get_type(type, size) \
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen t_buffer_get(sizeof(type) * (size))
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainenvoid *t_buffer_get(size_t size) ATTR_RETURNS_NONNULL;
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen/* Grow the buffer, memcpy()ing the memory to new location if needed. */
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen#define t_buffer_reget_type(buffer, type, size) \
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen t_buffer_reget(buffer, sizeof(type) * (size))
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenvoid *t_buffer_reget(void *buffer, size_t size) ATTR_RETURNS_NONNULL;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen/* Make the last t_buffer_get()ed buffer permanent. Note that size MUST be
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen less or equal than the size you gave with last t_buffer_get() or the
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen result will be undefined. */
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen#define t_buffer_alloc_type(type, size) \
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen t_buffer_alloc(sizeof(type) * (size))
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenvoid t_buffer_alloc(size_t size);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen/* Allocate the last t_buffer_get()ed data entirely. */
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainenvoid t_buffer_alloc_last_full(void);
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen/* If enabled, all the used memory is cleared after t_pop(). */
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenvoid data_stack_set_clean_after_pop(bool enable);
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenvoid data_stack_init(void);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenvoid data_stack_deinit(void);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#endif
3776ed607821b502468bdfd5a4533af3002125d1Timo Sirainen