Line data Source code
1 : #include "mem.h"
2 : #include <stdarg.h>
3 : #include <string.h>
4 : #include <limits.h>
5 :
6 : /**
7 : * @brief Round a byte size up to the allocator block boundary.
8 : *
9 : * Aligns @p requested_bytes to the next multiple of @ref MEMORY_BLOCK_BYTES, preserving zero
10 : * and guarding against overflow. Returns non-zero on error so callers can log and fail early.
11 : *
12 : * @param requested_bytes Number of bytes requested by the caller.
13 : * @param aligned_bytes Output pointer receiving the aligned size when successful.
14 : * @return 0 on success; non-zero if @p aligned_bytes is NULL or an overflow occurred.
15 : */
16 2042 : static int align_to_block_boundary(
17 : size_t requested_bytes,
18 : size_t *aligned_bytes)
19 : {
20 2042 : if(aligned_bytes == NULL)
21 : {
22 0 : return 1;
23 : }
24 :
25 2042 : if(requested_bytes == 0)
26 : {
27 0 : *aligned_bytes = 0;
28 0 : return 0;
29 : }
30 :
31 2042 : const size_t remainder = requested_bytes % MEMORY_BLOCK_BYTES;
32 :
33 2042 : if(remainder == 0)
34 : {
35 78 : *aligned_bytes = requested_bytes;
36 78 : return 0;
37 : }
38 :
39 1964 : const size_t padding = MEMORY_BLOCK_BYTES - remainder;
40 :
41 1964 : if(requested_bytes > SIZE_MAX - padding)
42 : {
43 0 : return 1;
44 : }
45 :
46 1964 : *aligned_bytes = requested_bytes + padding;
47 1964 : return 0;
48 : }
49 :
50 2069 : Return memory_resize(
51 : memory *memory_structure,
52 : size_t new_count,
53 : ...)
54 : {
55 : /** Return status
56 : * The status that will be passed to return() before exiting
57 : * By default, the function worked without errors
58 : */
59 2069 : Return status = SUCCESS;
60 2069 : unsigned char behavior_flags = 0U;
61 :
62 : va_list optional_arguments;
63 2069 : va_start(optional_arguments,new_count);
64 2069 : const unsigned int provided_flags = va_arg(optional_arguments,unsigned int);
65 :
66 2069 : if(provided_flags == UCHAR_MAX)
67 : {
68 2068 : behavior_flags = 0U;
69 : } else {
70 1 : behavior_flags = (unsigned char)provided_flags;
71 1 : const unsigned int terminator = va_arg(optional_arguments,unsigned int);
72 :
73 1 : if(terminator != UCHAR_MAX)
74 : {
75 0 : slog(ERROR,"Memory management; Resize flags terminator missing");
76 0 : status = FAILURE;
77 : }
78 : }
79 2069 : va_end(optional_arguments);
80 :
81 2069 : const bool zero_new_memory = (behavior_flags & ZERO_NEW_MEMORY) != 0U;
82 2069 : const bool allow_shrink = (behavior_flags & RELEASE_UNUSED) != 0U;
83 :
84 2069 : if(SUCCESS == status)
85 : {
86 2069 : if(memory_structure == NULL || memory_structure->element_size == 0)
87 : {
88 0 : slog(ERROR,"Memory management; Descriptor is NULL or not initialized");
89 0 : provide(FAILURE);
90 : }
91 : }
92 :
93 2069 : size_t previous_effective_bytes = 0;
94 2069 : size_t previous_allocated_bytes = 0;
95 2069 : size_t previous_elements = 0;
96 2069 : size_t previous_alignment_overhead = 0;
97 2069 : size_t total_size_in_bytes = 0;
98 :
99 2069 : if(SUCCESS == status)
100 : {
101 2069 : previous_elements = memory_structure->length;
102 2069 : previous_allocated_bytes = memory_structure->actually_allocated_bytes;
103 2069 : previous_effective_bytes = 0;
104 :
105 2069 : run(memory_guarded_size(memory_structure->element_size,
106 : previous_elements,
107 : &previous_effective_bytes));
108 :
109 2069 : if(previous_allocated_bytes > previous_effective_bytes)
110 : {
111 799 : previous_alignment_overhead = previous_allocated_bytes - previous_effective_bytes;
112 : }
113 : }
114 :
115 2069 : if(SUCCESS == status && new_count == previous_elements)
116 : {
117 27 : if(new_count == 0)
118 : {
119 1 : if(memory_structure->data == NULL)
120 : {
121 1 : telemetry_realloc_noop_counter();
122 1 : telemetry_noop_resize_event();
123 1 : provide(status);
124 : }
125 : } else {
126 26 : telemetry_realloc_noop_counter();
127 26 : telemetry_noop_resize_event();
128 26 : provide(status);
129 : }
130 : }
131 :
132 2042 : telemetry_reset_noop_streak();
133 :
134 2042 : run(memory_guarded_size(memory_structure->element_size,new_count,&total_size_in_bytes));
135 :
136 2042 : if(FAILURE == status)
137 : {
138 0 : slog(ERROR,
139 : "Memory management; Overflow for length=%zu (element_size=%zu)",
140 : new_count,
141 : memory_structure->element_size);
142 : }
143 :
144 2042 : if(SUCCESS == status)
145 : {
146 2042 : if(new_count == 0)
147 : {
148 0 : run(del(memory_structure));
149 : } else {
150 2042 : size_t aligned_size_in_bytes = 0;
151 :
152 2042 : if(align_to_block_boundary(total_size_in_bytes,&aligned_size_in_bytes) != 0)
153 : {
154 0 : slog(ERROR,
155 : "Memory management; Allocation alignment overflow for %zu bytes",
156 : total_size_in_bytes);
157 0 : status = FAILURE;
158 : } else {
159 2042 : const bool needs_fresh_allocation = memory_structure->data == NULL;
160 2042 : const bool needs_growth = aligned_size_in_bytes > memory_structure->actually_allocated_bytes;
161 2042 : const bool should_shrink = allow_shrink &&
162 0 : aligned_size_in_bytes < memory_structure->actually_allocated_bytes;
163 :
164 2042 : if(needs_fresh_allocation || needs_growth || should_shrink)
165 1294 : {
166 1294 : void *resized_pointer = NULL;
167 :
168 1294 : if(needs_fresh_allocation)
169 : {
170 1268 : resized_pointer = malloc(aligned_size_in_bytes);
171 : } else {
172 26 : resized_pointer = realloc(memory_structure->data,aligned_size_in_bytes);
173 : }
174 :
175 1294 : if(resized_pointer == NULL)
176 : {
177 0 : slog(ERROR,
178 : "Memory management; Memory allocation failed for %zu bytes",
179 : aligned_size_in_bytes);
180 0 : status = FAILURE;
181 :
182 0 : if(needs_fresh_allocation)
183 : {
184 0 : telemetry_allocation_failure();
185 : } else {
186 0 : telemetry_reallocation_failure();
187 : }
188 : } else {
189 1294 : memory_structure->data = resized_pointer;
190 :
191 1294 : if(needs_fresh_allocation)
192 : {
193 1268 : telemetry_active_descriptor_acquire();
194 1268 : telemetry_new_allocations_counter();
195 :
196 1268 : if(aligned_size_in_bytes > 0)
197 : {
198 1268 : telemetry_add(aligned_size_in_bytes);
199 : }
200 26 : } else if(needs_growth){
201 26 : telemetry_aligned_reallocations_counter();
202 26 : telemetry_add(aligned_size_in_bytes - previous_allocated_bytes);
203 0 : } else if(should_shrink){
204 0 : telemetry_aligned_reallocations_counter();
205 0 : const size_t reclaimed_bytes = previous_allocated_bytes - aligned_size_in_bytes;
206 :
207 0 : if(reclaimed_bytes > 0)
208 : {
209 0 : telemetry_release_unused_operation();
210 0 : telemetry_release_unused_bytes(reclaimed_bytes);
211 0 : telemetry_reduce(reclaimed_bytes);
212 0 : telemetry_free_total_bytes(reclaimed_bytes);
213 : }
214 : }
215 :
216 1294 : memory_structure->actually_allocated_bytes = aligned_size_in_bytes;
217 : }
218 : } else {
219 748 : telemetry_realloc_optimized_counter();
220 : }
221 :
222 2042 : if(SUCCESS == status)
223 : {
224 2042 : size_t bytes_to_zero = 0;
225 :
226 2042 : if(zero_new_memory && total_size_in_bytes > previous_effective_bytes)
227 : {
228 1 : bytes_to_zero = total_size_in_bytes - previous_effective_bytes;
229 : }
230 :
231 2042 : memory_structure->length = new_count;
232 :
233 2042 : if(bytes_to_zero > 0)
234 : {
235 1 : unsigned char *byte_view = (unsigned char *)memory_structure->data;
236 :
237 1 : if(byte_view == NULL)
238 : {
239 0 : slog(ERROR,"Memory management; Data pointer is NULL during zero-fill");
240 0 : status = FAILURE;
241 : } else {
242 1 : memset(byte_view + previous_effective_bytes,0,bytes_to_zero);
243 1 : telemetry_new_callocations_counter();
244 : }
245 : }
246 :
247 2042 : if(SUCCESS == status)
248 : {
249 2042 : if(total_size_in_bytes > previous_effective_bytes)
250 : {
251 1828 : telemetry_effective_add(total_size_in_bytes - previous_effective_bytes);
252 214 : } else if(total_size_in_bytes < previous_effective_bytes){
253 214 : telemetry_effective_reduce(previous_effective_bytes - total_size_in_bytes);
254 : }
255 : }
256 : }
257 : }
258 : }
259 : }
260 :
261 2042 : if(SUCCESS == status && new_count != 0)
262 : {
263 2042 : const size_t resulting_allocated_bytes = memory_structure->actually_allocated_bytes;
264 2042 : size_t new_alignment_overhead = 0;
265 :
266 2042 : if(resulting_allocated_bytes > total_size_in_bytes)
267 : {
268 1964 : new_alignment_overhead = resulting_allocated_bytes - total_size_in_bytes;
269 : }
270 :
271 2042 : if(new_alignment_overhead > previous_alignment_overhead)
272 : {
273 1425 : telemetry_alignment_overhead_add(new_alignment_overhead - previous_alignment_overhead);
274 617 : } else if(new_alignment_overhead < previous_alignment_overhead){
275 539 : telemetry_alignment_overhead_reduce(previous_alignment_overhead - new_alignment_overhead);
276 : }
277 : }
278 :
279 2042 : provide(status);
280 : }
|