1 #ifndef DATA_STACK_H
2 #define DATA_STACK_H
3 
4 /* Data stack makes it very easy to implement functions returning dynamic data
5    without having to worry much about memory management like freeing the
6    result or having large enough buffers for the result.
7 
8    t_ prefix was chosen to describe functions allocating memory from data
9    stack. "t" meaning temporary.
10 
11    Advantages over control stack and alloca():
12     - Functions can return a value allocated from data stack
13     - We can portably specify how much data we want to allocate at runtime
14 
15    Advantages over malloc():
16     - FAST, most of the time allocating memory means only updating a couple of
17       pointers and integers. Freeing the memory all at once also is a fast
18       operation.
19     - No need to free() each allocation resulting in prettier code
20     - No memory leaks
21     - No memory fragmentation
22 
23    Disadvantages:
24     - Allocating memory inside loops can accidentally allocate a lot of memory
25       if the loops are long and you forgot to place t_push() and t_pop() there.
26     - t_malloc()ed data could be accidentally stored into permanent location
27       and accessed after it's already been freed. const'ing the return values
28       helps for most uses though (see the t_malloc() description).
29     - Debugging invalid memory usage may be difficult using existing tools,
30       although compiling with DEBUG enabled helps finding simple buffer
31       overflows.
32 */
33 
34 #ifndef STATIC_CHECKER
35 typedef unsigned int data_stack_frame_t;
36 #else
37 typedef struct data_stack_frame *data_stack_frame_t;
38 #endif
39 
40 extern unsigned int data_stack_frame_id;
41 
42 /* All t_..() allocations between t_push*() and t_pop() are freed after t_pop()
43    is called. Returns the current stack frame number, which can be used
44    to detect missing t_pop() calls:
45 
46    x = t_push(marker); .. if (!t_pop(x)) abort();
47 
48    In DEBUG mode, t_push_named() makes a temporary allocation for the name,
49    but is safe to call in a loop as it performs the allocation within its own
50    frame. However, you should always prefer to use T_BEGIN { ... } T_END below.
51 */
52 data_stack_frame_t t_push(const char *marker) ATTR_HOT;
53 data_stack_frame_t t_push_named(const char *format, ...) ATTR_HOT ATTR_FORMAT(1, 2);
54 /* Returns TRUE on success, FALSE if t_pop() call was leaked. The caller
55    should panic. */
56 bool t_pop(data_stack_frame_t *id) ATTR_HOT;
57 /* Same as t_pop(), but move str out of the stack frame if it is inside.
58    This can be used to easily move e.g. error strings outside stack frames. */
59 bool t_pop_pass_str(data_stack_frame_t *id, const char **str);
60 
61 /* Pop the last data stack frame. This shouldn't be called outside test code. */
62 void t_pop_last_unsafe(void);
63 
64 /* Usage: T_BEGIN { code } T_END */
65 #define T_STRING(x)	#x
66 #define T_XSTRING(x)	T_STRING(x)	/* expand and then stringify */
67 #define T_BEGIN \
68 	STMT_START { \
69 		data_stack_frame_t _data_stack_cur_id = t_push(__FILE__ ":" T_XSTRING(__LINE__));
70 #define T_END \
71 	STMT_START { \
72 		if (unlikely(!t_pop(&_data_stack_cur_id))) \
73 			i_panic("Leaked t_pop() call"); \
74 	} STMT_END; \
75 	} STMT_END
76 
77 /* Usage:
78    const char *error;
79    T_BEGIN {
80      ...
81      if (ret < 0)
82        error = t_strdup_printf("foo() failed: %m");
83    } T_END_PASS_STR_IF(ret < 0, &error);
84    // error is still valid
85 */
86 #define T_END_PASS_STR_IF(pass_condition, str) \
87 	STMT_START { \
88 		if (unlikely(!t_pop_pass_str(&_data_stack_cur_id, (pass_condition) ? (str) : NULL))) \
89 			i_panic("Leaked t_pop() call"); \
90 	} STMT_END; \
91 	} STMT_END
92 /*
93    Usage:
94    const char *result;
95    T_BEGIN {
96      ...
97      result = t_strdup_printf(...);
98    } T_END_PASS_STR(&result);
99    // result is still valid
100 */
101 #define T_END_PASS_STR(str) \
102 	T_END_PASS_STR_IF(TRUE, str)
103 
104 /* WARNING: Be careful when using these functions, it's too easy to
105    accidentally save the returned value somewhere permanently.
106 
107    You probably should never use these functions directly, rather
108    create functions that return 'const xxx*' types and use t_malloc()
109    internally in them. This is a lot safer, since usually compiler
110    warns if you try to place them in xxx*. See strfuncs.c for examples.
111 
112    t_malloc() calls never fail. If there's not enough memory left,
113    i_panic() will be called. */
114 void *t_malloc_no0(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL;
115 void *t_malloc0(size_t size) ATTR_MALLOC ATTR_RETURNS_NONNULL;
116 
117 /* Try growing allocated memory. Returns TRUE if successful. Works only
118    for last allocated memory in current stack frame. */
119 bool t_try_realloc(void *mem, size_t size);
120 
121 /* Returns the number of bytes available in data stack without allocating
122    more memory. */
123 size_t t_get_bytes_available(void) ATTR_PURE;
124 
125 #define t_new(type, count) \
126 	((type *) t_malloc0(MALLOC_MULTIPLY((unsigned int)sizeof(type), (count))) + \
127 	 COMPILE_ERROR_IF_TRUE(sizeof(type) > UINT_MAX))
128 
129 /* Returns pointer to a temporary buffer you can use. The buffer will be
130    invalid as soon as next t_malloc() is called!
131 
132    If you wish to grow the buffer, you must give the full wanted size
133    in the size parameter. If return value doesn't point to the same value
134    as last time, you need to memcpy() data from the old buffer to the
135    new one (or do some other trickery). See t_buffer_reget(). */
136 void *t_buffer_get(size_t size) ATTR_RETURNS_NONNULL;
137 
138 /* Grow the buffer, memcpy()ing the memory to new location if needed. */
139 void *t_buffer_reget(void *buffer, size_t size) ATTR_RETURNS_NONNULL;
140 
141 /* Make the last t_buffer_get()ed buffer permanent. Note that size MUST be
142    less or equal than the size you gave with last t_buffer_get() or the
143    result will be undefined. */
144 void t_buffer_alloc(size_t size);
145 /* Allocate the last t_buffer_get()ed data entirely. */
146 void t_buffer_alloc_last_full(void);
147 
148 /* Returns TRUE if ptr is allocated within the given data stack frame.
149    Currently this assert-crashes if the data stack frame isn't the latest. */
150 bool data_stack_frame_contains(data_stack_frame_t *id, const void *ptr);
151 
152 /* Returns the number of bytes malloc()ated for data stack. */
153 size_t data_stack_get_alloc_size(void);
154 /* Returns the number of bytes currently used in data stack. */
155 size_t data_stack_get_used_size(void);
156 
157 /* Free all the memory that is currently unused (i.e. reserved for growing
158    data stack quickly). */
159 void data_stack_free_unused(void);
160 
161 void data_stack_init(void);
162 void data_stack_deinit_event(void);
163 void data_stack_deinit(void);
164 
165 #endif
166