/src/opensips/mem/q_malloc_dyn.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2019 OpenSIPS Solutions |
3 | | * |
4 | | * This file is part of opensips, a free SIP server. |
5 | | * |
6 | | * opensips is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation; either version 2 of the License, or |
9 | | * (at your option) any later version |
10 | | * |
11 | | * opensips is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | /* for any questions on the complex ifdef logic, see mem/f_malloc_dyn.h */ |
22 | | |
23 | | /* returns 0 on success, -1 on error; |
24 | | * new_size < size & rounded-up already!*/ |
25 | | static inline |
26 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
27 | | int qm_split_frag_dbg(struct qm_block *qm, struct qm_frag *f, |
28 | | unsigned long new_size, const char *file, |
29 | | const char *func, unsigned int line) |
30 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
31 | | int qm_split_frag(struct qm_block *qm, struct qm_frag *f, |
32 | | unsigned long new_size) |
33 | | #else |
34 | | int qm_split_frag(struct qm_block *qm, struct qm_frag *f, |
35 | | unsigned long new_size, const char *file, const char *func, |
36 | | unsigned int line) |
37 | | #endif |
38 | 0 | { |
39 | 0 | unsigned long rest; |
40 | 0 | struct qm_frag *n; |
41 | 0 | struct qm_frag_end *end; |
42 | |
|
43 | 0 | rest=f->size-new_size; |
44 | | #ifdef MEM_FRAG_AVOIDANCE |
45 | | if ((rest> (FRAG_OVERHEAD+Q_MALLOC_OPTIMIZE))|| |
46 | | (rest>=(FRAG_OVERHEAD+new_size))){/* the residue fragm. is big enough*/ |
47 | | #else |
48 | 0 | if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){ |
49 | 0 | #endif |
50 | 0 | f->size=new_size; |
51 | | /*split the fragment*/ |
52 | 0 | end=FRAG_END(f); |
53 | 0 | end->size=new_size; |
54 | 0 | n=(struct qm_frag*)(end+1); |
55 | 0 | n->size=rest-FRAG_OVERHEAD; |
56 | 0 | FRAG_END(n)->size=n->size; |
57 | 0 | FRAG_CLEAR_USED(n); /* never used */ |
58 | 0 | #if defined(DBG_MALLOC) || defined(STATISTICS) |
59 | 0 | qm->used-=FRAG_OVERHEAD; |
60 | 0 | #endif |
61 | | #ifdef DBG_MALLOC |
62 | | end->check1=END_CHECK_PATTERN1; |
63 | | end->check2=END_CHECK_PATTERN2; |
64 | | /* frag created by malloc, mark it*/ |
65 | | n->check=ST_CHECK_PATTERN; |
66 | | qm_dbg_clear(n); |
67 | | qm_dbg_fill(n, file, func, line); |
68 | | #endif |
69 | | /* reinsert n in free list*/ |
70 | 0 | qm_insert_free(qm, n); |
71 | 0 | return 0; |
72 | 0 | }else{ |
73 | | /* we cannot split this fragment any more */ |
74 | 0 | return -1; |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | |
79 | | |
80 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
81 | | void *qm_malloc_dbg(struct qm_block *qm, unsigned long size, |
82 | | const char *file, const char *func, unsigned int line) |
83 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
84 | | void *qm_malloc(struct qm_block *qm, unsigned long size) |
85 | | #else |
86 | | void *qm_malloc(struct qm_block *qm, unsigned long size, |
87 | | const char *file, const char *func, unsigned int line) |
88 | | #endif |
89 | 0 | { |
90 | 0 | struct qm_frag *f; |
91 | 0 | int hash; |
92 | |
|
93 | | #if defined DBG_MALLOC || defined Q_MALLOC_DYN |
94 | | unsigned int list_cntr; |
95 | | list_cntr = 0; |
96 | | #endif |
97 | |
|
98 | | #if defined DBG_MALLOC |
99 | | LM_GEN1(memlog, "%s_malloc (%lu), called from %s: %s(%d)\n", |
100 | | qm->name, size, file, func, line); |
101 | | #endif |
102 | | |
103 | | /*size must be a multiple of 8*/ |
104 | 0 | size=ROUNDUP(size); |
105 | 0 | if (size>(qm->size-qm->real_used)) { |
106 | 0 | LM_ERR(oom_errorf, qm->name, qm->size - qm->real_used, size, |
107 | 0 | qm->name[0] == 'p' ? "M" : "m"); |
108 | 0 | pkg_threshold_check(); |
109 | 0 | return 0; |
110 | 0 | } |
111 | | |
112 | | /*search for a suitable free frag*/ |
113 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
114 | | if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){ |
115 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
116 | 0 | if ((f=qm_find_free(qm, size, &hash))!=0){ |
117 | | #else |
118 | | if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){ |
119 | | #endif |
120 | | /* we found it!*/ |
121 | | /*detach it from the free list*/ |
122 | | #ifdef DBG_MALLOC |
123 | | qm_debug_frag(qm, f); |
124 | | #endif |
125 | 0 | qm_detach_free(qm, f); |
126 | | /*mark it as "busy"*/ |
127 | 0 | f->u.is_free=0; |
128 | 0 | qm->free_hash[hash].no--; |
129 | | /* we ignore split return */ |
130 | |
|
131 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
132 | | qm_split_frag_dbg(qm, f, size, file, "qm_malloc frag", line); |
133 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
134 | | qm_split_frag(qm, f, size); |
135 | | #else |
136 | | qm_split_frag(qm, f, size, file, "qm_malloc frag", line); |
137 | | #endif |
138 | |
|
139 | 0 | if (qm->max_real_used<qm->real_used) |
140 | 0 | qm->max_real_used=qm->real_used; |
141 | |
|
142 | | #ifdef DBG_MALLOC |
143 | | qm_dbg_fill(f, file, func, line); |
144 | | f->check=ST_CHECK_PATTERN; |
145 | | /* FRAG_END(f)->check1=END_CHECK_PATTERN1; |
146 | | FRAG_END(f)->check2=END_CHECK_PATTERN2;*/ |
147 | | LM_GEN1(memlog, "%s_malloc(%lu), returns address %p frag. %p " |
148 | | "(size=%lu) on %d -th hit\n", |
149 | | qm->name, size, (char*)f+sizeof(struct qm_frag), f, f->size, list_cntr ); |
150 | | #endif |
151 | |
|
152 | 0 | pkg_threshold_check(); |
153 | 0 | qm->fragments += 1; |
154 | 0 | return (char*)f+sizeof(struct qm_frag); |
155 | 0 | } |
156 | | |
157 | 0 | LM_ERR(oom_errorf, qm->name, qm->size - qm->real_used, size, |
158 | 0 | qm->name[0] == 'p' ? "M" : "m"); |
159 | 0 | pkg_threshold_check(); |
160 | 0 | return 0; |
161 | 0 | } |
162 | | |
163 | | |
164 | | |
165 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
166 | | void qm_free_dbg(struct qm_block *qm, void *p, |
167 | | const char *file, const char *func, unsigned int line) |
168 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
169 | | void qm_free(struct qm_block *qm, void *p) |
170 | | #else |
171 | | void qm_free(struct qm_block *qm, void *p, |
172 | | const char *file, const char *func, unsigned int line) |
173 | | #endif |
174 | 0 | { |
175 | 0 | struct qm_frag *f; |
176 | 0 | struct qm_frag *prev; |
177 | 0 | struct qm_frag *next; |
178 | 0 | unsigned long size; |
179 | |
|
180 | | #ifdef DBG_MALLOC |
181 | | LM_GEN1(memlog, "%s_free(%p), called from %s: %s(%d)\n", |
182 | | qm->name, p, file, func, line); |
183 | | #endif |
184 | 0 | if (!p) { |
185 | 0 | LM_GEN1(memlog, "free(NULL) called\n"); |
186 | 0 | return; |
187 | 0 | } |
188 | | #ifdef DBG_MALLOC |
189 | | if (p>(void*)qm->last_frag_end || p<(void*)qm->first_frag){ |
190 | | LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p); |
191 | | abort(); |
192 | | } |
193 | | #endif |
194 | 0 | f=QM_FRAG(p); |
195 | | #ifdef DBG_MALLOC |
196 | | qm_debug_frag(qm, f); |
197 | | if (f->u.is_free){ |
198 | | LM_CRIT("freeing already freed pointer," |
199 | | " first free: %s: %s(%ld) - aborting\n", |
200 | | qm_dbg_coords(f)); |
201 | | abort(); |
202 | | } |
203 | | LM_GEN1( memlog, "freeing frag. %p alloc'ed from %s: %s(%ld)\n", |
204 | | f, qm_dbg_coords(f)); |
205 | | #endif |
206 | |
|
207 | 0 | size=f->size; |
208 | | /* join packets if possible*/ |
209 | 0 | prev=next=0; |
210 | 0 | next=FRAG_NEXT(f); |
211 | 0 | if (((char*)next < (char*)qm->last_frag_end) &&( next->u.is_free)){ |
212 | | /* join */ |
213 | | #ifdef DBG_MALLOC |
214 | | qm_debug_frag(qm, next); |
215 | | #endif |
216 | 0 | qm_detach_free(qm, next); |
217 | 0 | size+=next->size+FRAG_OVERHEAD; |
218 | 0 | #if defined(DBG_MALLOC) || defined(STATISTICS) |
219 | 0 | qm->used+=FRAG_OVERHEAD; |
220 | 0 | #endif |
221 | 0 | qm->free_hash[GET_HASH(next->size)].no--; /* FIXME slow */ |
222 | 0 | } |
223 | |
|
224 | 0 | if (f > qm->first_frag){ |
225 | 0 | prev=FRAG_PREV(f); |
226 | | /* (struct qm_frag*)((char*)f - (struct qm_frag_end*)((char*)f- |
227 | | sizeof(struct qm_frag_end))->size);*/ |
228 | | #ifdef DBG_MALLOC |
229 | | qm_debug_frag(qm, prev); |
230 | | #endif |
231 | 0 | if (prev->u.is_free){ |
232 | | /*join*/ |
233 | 0 | qm_detach_free(qm, prev); |
234 | 0 | size+=prev->size+FRAG_OVERHEAD; |
235 | 0 | #if defined(DBG_MALLOC) || defined(STATISTICS) |
236 | 0 | qm->used+=FRAG_OVERHEAD; |
237 | 0 | #endif |
238 | 0 | qm->free_hash[GET_HASH(prev->size)].no--; /* FIXME slow */ |
239 | 0 | f=prev; |
240 | 0 | } |
241 | 0 | } |
242 | 0 | f->size=size; |
243 | 0 | FRAG_END(f)->size=f->size; |
244 | | #ifdef DBG_MALLOC |
245 | | qm_dbg_fill(f, file, func, line); |
246 | | #endif |
247 | 0 | qm_insert_free(qm, f); |
248 | 0 | qm->fragments -= 1; |
249 | 0 | pkg_threshold_check(); |
250 | 0 | } |
251 | | |
252 | | |
253 | | |
254 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
255 | | void *qm_realloc_dbg(struct qm_block *qm, void *p, unsigned long size, |
256 | | const char *file, const char *func, unsigned int line) |
257 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
258 | | void *qm_realloc(struct qm_block *qm, void *p, unsigned long size) |
259 | | #else |
260 | | void *qm_realloc(struct qm_block *qm, void *p, unsigned long size, |
261 | | const char *file, const char *func, unsigned int line) |
262 | | #endif |
263 | 0 | { |
264 | 0 | struct qm_frag *f; |
265 | 0 | unsigned long diff; |
266 | 0 | unsigned long orig_size; |
267 | 0 | struct qm_frag *n; |
268 | 0 | void *ptr; |
269 | |
|
270 | | #ifdef DBG_MALLOC |
271 | | LM_GEN1(memlog, "%s_realloc(%p, %lu->%lu), called from %s: %s(%d)\n", |
272 | | qm->name, p, p ? QM_FRAG(p)->size:0, size, file, func, line); |
273 | | if (p && (p > (void *)qm->last_frag_end || p < (void *)qm->first_frag)) { |
274 | | LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p); |
275 | | abort(); |
276 | | } |
277 | | #endif |
278 | |
|
279 | 0 | if (size == 0) { |
280 | 0 | if (p) |
281 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
282 | | qm_free_dbg(qm, p, file, func, line); |
283 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
284 | 0 | qm_free(qm, p); |
285 | | #else |
286 | | qm_free(qm, p, file, func, line); |
287 | | #endif |
288 | 0 | pkg_threshold_check(); |
289 | 0 | return 0; |
290 | 0 | } |
291 | | |
292 | 0 | if (!p) |
293 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
294 | | return qm_malloc_dbg(qm, size, file, func, line); |
295 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
296 | 0 | return qm_malloc(qm, size); |
297 | | #else |
298 | | return qm_malloc(qm, size, file, func, line); |
299 | | #endif |
300 | | |
301 | 0 | f = QM_FRAG(p); |
302 | |
|
303 | | #ifdef DBG_MALLOC |
304 | | qm_debug_frag(qm, f); |
305 | | LM_GEN1( memlog, "realloc'ing frag %p alloc'ed from %s: %s(%ld)\n", |
306 | | f, qm_dbg_coords(f)); |
307 | | if (f->u.is_free) { |
308 | | LM_CRIT("trying to realloc an already freed " |
309 | | "pointer %p , fragment %p -- aborting\n", p, f); |
310 | | abort(); |
311 | | } |
312 | | #endif |
313 | | |
314 | | /* find first acceptable size */ |
315 | 0 | size=ROUNDUP(size); |
316 | 0 | if (f->size > size) { |
317 | 0 | orig_size=f->size; |
318 | | /* shrink */ |
319 | | #ifdef DBG_MALLOC |
320 | | LM_GEN1(memlog,"shrinking from %lu to %lu\n", f->size, size); |
321 | | #endif |
322 | |
|
323 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
324 | | qm_split_frag_dbg(qm, f, size, file, "qm_realloc frag", line); |
325 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
326 | | qm_split_frag(qm, f, size); |
327 | | #else |
328 | | qm_split_frag(qm, f, size, file, "qm_realloc frag", line); |
329 | | #endif |
330 | |
|
331 | 0 | } else if (f->size < size) { |
332 | | /* grow */ |
333 | | #ifdef DBG_MALLOC |
334 | | LM_GEN1( memlog, "growing from %lu to %lu\n", f->size, size); |
335 | | #endif |
336 | |
|
337 | 0 | orig_size=f->size; |
338 | 0 | diff=size-f->size; |
339 | 0 | n=FRAG_NEXT(f); |
340 | 0 | if (((char*)n < (char*)qm->last_frag_end) && |
341 | 0 | (n->u.is_free)&&((n->size+FRAG_OVERHEAD)>=diff)){ |
342 | | /* join */ |
343 | 0 | qm_detach_free(qm, n); |
344 | 0 | qm->free_hash[GET_HASH(n->size)].no--; /*FIXME: slow*/ |
345 | 0 | f->size+=n->size+FRAG_OVERHEAD; |
346 | 0 | #if defined(DBG_MALLOC) || defined(STATISTICS) |
347 | 0 | qm->used+=FRAG_OVERHEAD; |
348 | 0 | #endif |
349 | 0 | FRAG_END(f)->size=f->size; |
350 | | /* end checks should be ok */ |
351 | | /* split it if necessary */ |
352 | 0 | if (f->size > size ) { |
353 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
354 | | qm_split_frag_dbg(qm, f, size, file, "qm_realloc frag", line); |
355 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
356 | | qm_split_frag(qm, f, size); |
357 | | #else |
358 | | qm_split_frag(qm, f, size, file, "qm_realloc frag", line); |
359 | | #endif |
360 | 0 | } |
361 | 0 | } else { |
362 | | /* could not join => realloc */ |
363 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
364 | | ptr = qm_malloc_dbg(qm, size, file, func, line); |
365 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
366 | | ptr = qm_malloc(qm, size); |
367 | | #else |
368 | | ptr = qm_malloc(qm, size, file, func, line); |
369 | | #endif |
370 | |
|
371 | 0 | if (ptr) { |
372 | | /* copy, need by libssl */ |
373 | 0 | memcpy(ptr, p, orig_size); |
374 | |
|
375 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
376 | | qm_free_dbg(qm, p, file, func, line); |
377 | | #elif !defined Q_MALLOC_DYN && !defined DBG_MALLOC |
378 | | qm_free(qm, p); |
379 | | #else |
380 | | qm_free(qm, p, file, func, line); |
381 | | #endif |
382 | 0 | } |
383 | |
|
384 | 0 | p = ptr; |
385 | 0 | } |
386 | 0 | } else { |
387 | | /* do nothing */ |
388 | | #ifdef DBG_MALLOC |
389 | | LM_GEN1(memlog,"doing nothing, same size: %lu - %lu\n", f->size, size); |
390 | | #endif |
391 | 0 | } |
392 | |
|
393 | | #ifdef DBG_MALLOC |
394 | | LM_GEN1(memlog,"returning %p\n", p); |
395 | | #endif |
396 | |
|
397 | 0 | pkg_threshold_check(); |
398 | 0 | return p; |
399 | 0 | } |
400 | | |
401 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
402 | | void qm_status_dbg(struct qm_block *qm) |
403 | | #else |
404 | | void qm_status(struct qm_block *qm) |
405 | | #endif |
406 | 0 | { |
407 | 0 | struct qm_frag *f; |
408 | 0 | int i,j; |
409 | 0 | int h; |
410 | 0 | int unused; |
411 | |
|
412 | | #ifdef DBG_MALLOC |
413 | | mem_dbg_htable_t allocd; |
414 | | struct mem_dbg_entry *it; |
415 | | #endif |
416 | |
|
417 | 0 | LM_GEN1(memdump, "qm_status (%p):\n", qm); |
418 | 0 | if (!qm) return; |
419 | | |
420 | 0 | #if defined(DBG_MALLOC) || defined(STATISTICS) |
421 | 0 | LM_GEN1(memdump, " heap size= %lu\n", qm->size); |
422 | 0 | LM_GEN1(memdump, " used= %lu, used+overhead=%lu, free=%lu\n", |
423 | 0 | qm->used, qm->real_used, qm->size-qm->real_used); |
424 | 0 | LM_GEN1(memdump, " max used (+overhead)= %lu\n", qm->max_real_used); |
425 | 0 | #endif |
426 | | |
427 | | #ifdef DBG_MALLOC |
428 | | dbg_ht_init(allocd); |
429 | | |
430 | | for (f = qm->first_frag; f >= qm->first_frag && |
431 | | (void *)f < (void *)qm->last_frag_end; f = FRAG_NEXT(f)) { |
432 | | if (!f->u.is_free && f->dbg[0].file) |
433 | | if (dbg_ht_update(allocd, qm_dbg_coords(f), f->size) < 0) { |
434 | | LM_ERR("Unable to update alloc'ed. memory summary\n"); |
435 | | dbg_ht_free(allocd); |
436 | | return; |
437 | | } |
438 | | } |
439 | | |
440 | | if ((void *)f != (void *)qm->last_frag_end) |
441 | | LM_GEN1(memdump, "failed to walk through all fragments (%p %p %p)\n", |
442 | | f, qm->first_frag, qm->last_frag_end); |
443 | | |
444 | | LM_GEN1(memdump, " dumping summary of all alloc'ed. fragments:\n"); |
445 | | LM_GEN1(memdump, "----------------------------------------------------\n"); |
446 | | LM_GEN1(memdump, "total_bytes | num_allocations x [file: func, line]\n"); |
447 | | LM_GEN1(memdump, "----------------------------------------------------\n"); |
448 | | for(i=0; i < DBG_HASH_SIZE; i++) { |
449 | | it = allocd[i]; |
450 | | while (it) { |
451 | | LM_GEN1(memdump, " %10lu : %lu x [%s: %s, line %lu]\n", |
452 | | it->size, it->no_fragments, it->file, it->func, it->line); |
453 | | it = it->next; |
454 | | } |
455 | | } |
456 | | LM_GEN1(memdump, "----------------------------------------------------\n"); |
457 | | |
458 | | dbg_ht_free(allocd); |
459 | | #endif |
460 | | |
461 | 0 | LM_GEN1(memdump, " dumping free list stats :\n"); |
462 | 0 | for(h=0,i=0;h<QM_HASH_SIZE;h++){ |
463 | 0 | unused=0; |
464 | 0 | for (f=qm->free_hash[h].head.u.nxt_free,j=0; |
465 | 0 | f!=&(qm->free_hash[h].head); f=f->u.nxt_free, i++, j++){ |
466 | 0 | if (!FRAG_WAS_USED(f)){ |
467 | 0 | unused++; |
468 | | #ifdef DBG_MALLOC |
469 | | LM_GEN1(memdump, "unused fragm.: hash = %3d, fragment %p," |
470 | | " address %p size %lu, created from %s: %s(%lu)\n", |
471 | | h, f, (char*)f+sizeof(struct qm_frag), f->size, |
472 | | qm_dbg_coords(f)); |
473 | | #endif |
474 | 0 | } |
475 | 0 | } |
476 | |
|
477 | 0 | if (j) LM_GEN1(memdump, "hash= %3d. fragments no.: %5d, unused: %5d\n" |
478 | 0 | "\t\t bucket size: %9lu - %9ld (first %9lu)\n", |
479 | 0 | h, j, unused, UN_HASH(h), |
480 | 0 | ((h<=Q_MALLOC_OPTIMIZE/QM_ROUNDTO)?1:2)*UN_HASH(h), |
481 | 0 | qm->free_hash[h].head.u.nxt_free->size |
482 | 0 | ); |
483 | 0 | if (j!=qm->free_hash[h].no){ |
484 | 0 | LM_CRIT("different free frag. count: %d!=%lu" |
485 | 0 | " for hash %3d\n", j, qm->free_hash[h].no, h); |
486 | 0 | } |
487 | |
|
488 | 0 | } |
489 | 0 | LM_GEN1(memdump, "-----------------------------\n"); |
490 | 0 | } |
491 | | |
492 | | #define Q_MALLOC_DYN |