Mercurial > vim
comparison src/alloc.c @ 25529:bb1097899693 v8.2.3301
patch 8.2.3301: memory allocation functions don't have their own place
Commit: https://github.com/vim/vim/commit/cbae5802832b29f3a1af4cb6b0fc8cf69f17cbf4
Author: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Fri Aug 6 21:51:55 2021 +0200
patch 8.2.3301: memory allocation functions don't have their own place
Problem: Memory allocation functions don't have their own place.
Solution: Move memory allocation functions to alloc.c. (Yegappan
Lakshmanan, closes #8717)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Fri, 06 Aug 2021 22:00:04 +0200 |
parents | |
children | 0082503ff2ff |
comparison
equal
deleted
inserted
replaced
25528:7407b745d43c | 25529:bb1097899693 |
---|---|
1 /* vi:set ts=8 sts=4 sw=4 noet: | |
2 * | |
3 * VIM - Vi IMproved by Bram Moolenaar | |
4 * | |
5 * Do ":help uganda" in Vim to read copying and usage conditions. | |
6 * Do ":help credits" in Vim to see a list of people who contributed. | |
7 * See README.txt for an overview of the Vim source code. | |
8 */ | |
9 | |
10 /* | |
11 * alloc.c: functions for memory management | |
12 */ | |
13 | |
14 #include "vim.h" | |
15 | |
16 /********************************************************************** | |
17 * Various routines dealing with allocation and deallocation of memory. | |
18 */ | |
19 | |
20 #if defined(MEM_PROFILE) || defined(PROTO) | |
21 | |
22 # define MEM_SIZES 8200 | |
23 static long_u mem_allocs[MEM_SIZES]; | |
24 static long_u mem_frees[MEM_SIZES]; | |
25 static long_u mem_allocated; | |
26 static long_u mem_freed; | |
27 static long_u mem_peak; | |
28 static long_u num_alloc; | |
29 static long_u num_freed; | |
30 | |
31 static void | |
32 mem_pre_alloc_s(size_t *sizep) | |
33 { | |
34 *sizep += sizeof(size_t); | |
35 } | |
36 | |
37 static void | |
38 mem_pre_alloc_l(size_t *sizep) | |
39 { | |
40 *sizep += sizeof(size_t); | |
41 } | |
42 | |
43 static void | |
44 mem_post_alloc( | |
45 void **pp, | |
46 size_t size) | |
47 { | |
48 if (*pp == NULL) | |
49 return; | |
50 size -= sizeof(size_t); | |
51 *(long_u *)*pp = size; | |
52 if (size <= MEM_SIZES-1) | |
53 mem_allocs[size-1]++; | |
54 else | |
55 mem_allocs[MEM_SIZES-1]++; | |
56 mem_allocated += size; | |
57 if (mem_allocated - mem_freed > mem_peak) | |
58 mem_peak = mem_allocated - mem_freed; | |
59 num_alloc++; | |
60 *pp = (void *)((char *)*pp + sizeof(size_t)); | |
61 } | |
62 | |
63 static void | |
64 mem_pre_free(void **pp) | |
65 { | |
66 long_u size; | |
67 | |
68 *pp = (void *)((char *)*pp - sizeof(size_t)); | |
69 size = *(size_t *)*pp; | |
70 if (size <= MEM_SIZES-1) | |
71 mem_frees[size-1]++; | |
72 else | |
73 mem_frees[MEM_SIZES-1]++; | |
74 mem_freed += size; | |
75 num_freed++; | |
76 } | |
77 | |
78 /* | |
79 * called on exit via atexit() | |
80 */ | |
81 void | |
82 vim_mem_profile_dump(void) | |
83 { | |
84 int i, j; | |
85 | |
86 printf("\r\n"); | |
87 j = 0; | |
88 for (i = 0; i < MEM_SIZES - 1; i++) | |
89 { | |
90 if (mem_allocs[i] || mem_frees[i]) | |
91 { | |
92 if (mem_frees[i] > mem_allocs[i]) | |
93 printf("\r\n%s", _("ERROR: ")); | |
94 printf("[%4d / %4lu-%-4lu] ", i + 1, mem_allocs[i], mem_frees[i]); | |
95 j++; | |
96 if (j > 3) | |
97 { | |
98 j = 0; | |
99 printf("\r\n"); | |
100 } | |
101 } | |
102 } | |
103 | |
104 i = MEM_SIZES - 1; | |
105 if (mem_allocs[i]) | |
106 { | |
107 printf("\r\n"); | |
108 if (mem_frees[i] > mem_allocs[i]) | |
109 puts(_("ERROR: ")); | |
110 printf("[>%d / %4lu-%-4lu]", i, mem_allocs[i], mem_frees[i]); | |
111 } | |
112 | |
113 printf(_("\n[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n"), | |
114 mem_allocated, mem_freed, mem_allocated - mem_freed, mem_peak); | |
115 printf(_("[calls] total re/malloc()'s %lu, total free()'s %lu\n\n"), | |
116 num_alloc, num_freed); | |
117 } | |
118 | |
119 #endif // MEM_PROFILE | |
120 | |
121 #ifdef FEAT_EVAL | |
122 int | |
123 alloc_does_fail(size_t size) | |
124 { | |
125 if (alloc_fail_countdown == 0) | |
126 { | |
127 if (--alloc_fail_repeat <= 0) | |
128 alloc_fail_id = 0; | |
129 do_outofmem_msg(size); | |
130 return TRUE; | |
131 } | |
132 --alloc_fail_countdown; | |
133 return FALSE; | |
134 } | |
135 #endif | |
136 | |
137 /* | |
138 * Some memory is reserved for error messages and for being able to | |
139 * call mf_release_all(), which needs some memory for mf_trans_add(). | |
140 */ | |
141 #define KEEP_ROOM (2 * 8192L) | |
142 #define KEEP_ROOM_KB (KEEP_ROOM / 1024L) | |
143 | |
144 /* | |
145 * The normal way to allocate memory. This handles an out-of-memory situation | |
146 * as well as possible, still returns NULL when we're completely out. | |
147 */ | |
148 void * | |
149 alloc(size_t size) | |
150 { | |
151 return lalloc(size, TRUE); | |
152 } | |
153 | |
154 /* | |
155 * alloc() with an ID for alloc_fail(). | |
156 */ | |
157 void * | |
158 alloc_id(size_t size, alloc_id_T id UNUSED) | |
159 { | |
160 #ifdef FEAT_EVAL | |
161 if (alloc_fail_id == id && alloc_does_fail(size)) | |
162 return NULL; | |
163 #endif | |
164 return lalloc(size, TRUE); | |
165 } | |
166 | |
167 /* | |
168 * Allocate memory and set all bytes to zero. | |
169 */ | |
170 void * | |
171 alloc_clear(size_t size) | |
172 { | |
173 void *p; | |
174 | |
175 p = lalloc(size, TRUE); | |
176 if (p != NULL) | |
177 (void)vim_memset(p, 0, size); | |
178 return p; | |
179 } | |
180 | |
181 /* | |
182 * Same as alloc_clear() but with allocation id for testing | |
183 */ | |
184 void * | |
185 alloc_clear_id(size_t size, alloc_id_T id UNUSED) | |
186 { | |
187 #ifdef FEAT_EVAL | |
188 if (alloc_fail_id == id && alloc_does_fail(size)) | |
189 return NULL; | |
190 #endif | |
191 return alloc_clear(size); | |
192 } | |
193 | |
194 /* | |
195 * Allocate memory like lalloc() and set all bytes to zero. | |
196 */ | |
197 void * | |
198 lalloc_clear(size_t size, int message) | |
199 { | |
200 void *p; | |
201 | |
202 p = lalloc(size, message); | |
203 if (p != NULL) | |
204 (void)vim_memset(p, 0, size); | |
205 return p; | |
206 } | |
207 | |
208 /* | |
209 * Low level memory allocation function. | |
210 * This is used often, KEEP IT FAST! | |
211 */ | |
212 void * | |
213 lalloc(size_t size, int message) | |
214 { | |
215 void *p; // pointer to new storage space | |
216 static int releasing = FALSE; // don't do mf_release_all() recursive | |
217 int try_again; | |
218 #if defined(HAVE_AVAIL_MEM) | |
219 static size_t allocated = 0; // allocated since last avail check | |
220 #endif | |
221 | |
222 // Safety check for allocating zero bytes | |
223 if (size == 0) | |
224 { | |
225 // Don't hide this message | |
226 emsg_silent = 0; | |
227 iemsg(_("E341: Internal error: lalloc(0, )")); | |
228 return NULL; | |
229 } | |
230 | |
231 #ifdef MEM_PROFILE | |
232 mem_pre_alloc_l(&size); | |
233 #endif | |
234 | |
235 /* | |
236 * Loop when out of memory: Try to release some memfile blocks and | |
237 * if some blocks are released call malloc again. | |
238 */ | |
239 for (;;) | |
240 { | |
241 /* | |
242 * Handle three kind of systems: | |
243 * 1. No check for available memory: Just return. | |
244 * 2. Slow check for available memory: call mch_avail_mem() after | |
245 * allocating KEEP_ROOM amount of memory. | |
246 * 3. Strict check for available memory: call mch_avail_mem() | |
247 */ | |
248 if ((p = malloc(size)) != NULL) | |
249 { | |
250 #ifndef HAVE_AVAIL_MEM | |
251 // 1. No check for available memory: Just return. | |
252 goto theend; | |
253 #else | |
254 // 2. Slow check for available memory: call mch_avail_mem() after | |
255 // allocating (KEEP_ROOM / 2) amount of memory. | |
256 allocated += size; | |
257 if (allocated < KEEP_ROOM / 2) | |
258 goto theend; | |
259 allocated = 0; | |
260 | |
261 // 3. check for available memory: call mch_avail_mem() | |
262 if (mch_avail_mem(TRUE) < KEEP_ROOM_KB && !releasing) | |
263 { | |
264 free(p); // System is low... no go! | |
265 p = NULL; | |
266 } | |
267 else | |
268 goto theend; | |
269 #endif | |
270 } | |
271 /* | |
272 * Remember that mf_release_all() is being called to avoid an endless | |
273 * loop, because mf_release_all() may call alloc() recursively. | |
274 */ | |
275 if (releasing) | |
276 break; | |
277 releasing = TRUE; | |
278 | |
279 clear_sb_text(TRUE); // free any scrollback text | |
280 try_again = mf_release_all(); // release as many blocks as possible | |
281 | |
282 releasing = FALSE; | |
283 if (!try_again) | |
284 break; | |
285 } | |
286 | |
287 if (message && p == NULL) | |
288 do_outofmem_msg(size); | |
289 | |
290 theend: | |
291 #ifdef MEM_PROFILE | |
292 mem_post_alloc(&p, size); | |
293 #endif | |
294 return p; | |
295 } | |
296 | |
297 /* | |
298 * lalloc() with an ID for alloc_fail(). | |
299 */ | |
300 #if defined(FEAT_SIGNS) || defined(PROTO) | |
301 void * | |
302 lalloc_id(size_t size, int message, alloc_id_T id UNUSED) | |
303 { | |
304 #ifdef FEAT_EVAL | |
305 if (alloc_fail_id == id && alloc_does_fail(size)) | |
306 return NULL; | |
307 #endif | |
308 return (lalloc(size, message)); | |
309 } | |
310 #endif | |
311 | |
312 #if defined(MEM_PROFILE) || defined(PROTO) | |
313 /* | |
314 * realloc() with memory profiling. | |
315 */ | |
316 void * | |
317 mem_realloc(void *ptr, size_t size) | |
318 { | |
319 void *p; | |
320 | |
321 mem_pre_free(&ptr); | |
322 mem_pre_alloc_s(&size); | |
323 | |
324 p = realloc(ptr, size); | |
325 | |
326 mem_post_alloc(&p, size); | |
327 | |
328 return p; | |
329 } | |
330 #endif | |
331 | |
332 /* | |
333 * Avoid repeating the error message many times (they take 1 second each). | |
334 * Did_outofmem_msg is reset when a character is read. | |
335 */ | |
336 void | |
337 do_outofmem_msg(size_t size) | |
338 { | |
339 if (!did_outofmem_msg) | |
340 { | |
341 // Don't hide this message | |
342 emsg_silent = 0; | |
343 | |
344 // Must come first to avoid coming back here when printing the error | |
345 // message fails, e.g. when setting v:errmsg. | |
346 did_outofmem_msg = TRUE; | |
347 | |
348 semsg(_("E342: Out of memory! (allocating %lu bytes)"), (long_u)size); | |
349 | |
350 if (starting == NO_SCREEN) | |
351 // Not even finished with initializations and already out of | |
352 // memory? Then nothing is going to work, exit. | |
353 mch_exit(123); | |
354 } | |
355 } | |
356 | |
357 #if defined(EXITFREE) || defined(PROTO) | |
358 | |
359 /* | |
360 * Free everything that we allocated. | |
361 * Can be used to detect memory leaks, e.g., with ccmalloc. | |
362 * NOTE: This is tricky! Things are freed that functions depend on. Don't be | |
363 * surprised if Vim crashes... | |
364 * Some things can't be freed, esp. things local to a library function. | |
365 */ | |
366 void | |
367 free_all_mem(void) | |
368 { | |
369 buf_T *buf, *nextbuf; | |
370 | |
371 // When we cause a crash here it is caught and Vim tries to exit cleanly. | |
372 // Don't try freeing everything again. | |
373 if (entered_free_all_mem) | |
374 return; | |
375 entered_free_all_mem = TRUE; | |
376 // Don't want to trigger autocommands from here on. | |
377 block_autocmds(); | |
378 | |
379 // Close all tabs and windows. Reset 'equalalways' to avoid redraws. | |
380 p_ea = FALSE; | |
381 if (first_tabpage != NULL && first_tabpage->tp_next != NULL) | |
382 do_cmdline_cmd((char_u *)"tabonly!"); | |
383 if (!ONE_WINDOW) | |
384 do_cmdline_cmd((char_u *)"only!"); | |
385 | |
386 # if defined(FEAT_SPELL) | |
387 // Free all spell info. | |
388 spell_free_all(); | |
389 # endif | |
390 | |
391 # if defined(FEAT_BEVAL_TERM) | |
392 ui_remove_balloon(); | |
393 # endif | |
394 # ifdef FEAT_PROP_POPUP | |
395 if (curwin != NULL) | |
396 close_all_popups(TRUE); | |
397 # endif | |
398 | |
399 // Clear user commands (before deleting buffers). | |
400 ex_comclear(NULL); | |
401 | |
402 // When exiting from mainerr_arg_missing curbuf has not been initialized, | |
403 // and not much else. | |
404 if (curbuf != NULL) | |
405 { | |
406 # ifdef FEAT_MENU | |
407 // Clear menus. | |
408 do_cmdline_cmd((char_u *)"aunmenu *"); | |
409 # ifdef FEAT_MULTI_LANG | |
410 do_cmdline_cmd((char_u *)"menutranslate clear"); | |
411 # endif | |
412 # endif | |
413 // Clear mappings, abbreviations, breakpoints. | |
414 do_cmdline_cmd((char_u *)"lmapclear"); | |
415 do_cmdline_cmd((char_u *)"xmapclear"); | |
416 do_cmdline_cmd((char_u *)"mapclear"); | |
417 do_cmdline_cmd((char_u *)"mapclear!"); | |
418 do_cmdline_cmd((char_u *)"abclear"); | |
419 # if defined(FEAT_EVAL) | |
420 do_cmdline_cmd((char_u *)"breakdel *"); | |
421 # endif | |
422 # if defined(FEAT_PROFILE) | |
423 do_cmdline_cmd((char_u *)"profdel *"); | |
424 # endif | |
425 # if defined(FEAT_KEYMAP) | |
426 do_cmdline_cmd((char_u *)"set keymap="); | |
427 # endif | |
428 } | |
429 | |
430 # ifdef FEAT_TITLE | |
431 free_titles(); | |
432 # endif | |
433 # if defined(FEAT_SEARCHPATH) | |
434 free_findfile(); | |
435 # endif | |
436 | |
437 // Obviously named calls. | |
438 free_all_autocmds(); | |
439 clear_termcodes(); | |
440 free_all_marks(); | |
441 alist_clear(&global_alist); | |
442 free_homedir(); | |
443 free_users(); | |
444 free_search_patterns(); | |
445 free_old_sub(); | |
446 free_last_insert(); | |
447 free_insexpand_stuff(); | |
448 free_prev_shellcmd(); | |
449 free_regexp_stuff(); | |
450 free_tag_stuff(); | |
451 free_cd_dir(); | |
452 # ifdef FEAT_SIGNS | |
453 free_signs(); | |
454 # endif | |
455 # ifdef FEAT_EVAL | |
456 set_expr_line(NULL, NULL); | |
457 # endif | |
458 # ifdef FEAT_DIFF | |
459 if (curtab != NULL) | |
460 diff_clear(curtab); | |
461 # endif | |
462 clear_sb_text(TRUE); // free any scrollback text | |
463 | |
464 // Free some global vars. | |
465 free_username(); | |
466 # ifdef FEAT_CLIPBOARD | |
467 vim_regfree(clip_exclude_prog); | |
468 # endif | |
469 vim_free(last_cmdline); | |
470 vim_free(new_last_cmdline); | |
471 set_keep_msg(NULL, 0); | |
472 | |
473 // Clear cmdline history. | |
474 p_hi = 0; | |
475 init_history(); | |
476 # ifdef FEAT_PROP_POPUP | |
477 clear_global_prop_types(); | |
478 # endif | |
479 | |
480 # ifdef FEAT_QUICKFIX | |
481 { | |
482 win_T *win; | |
483 tabpage_T *tab; | |
484 | |
485 qf_free_all(NULL); | |
486 // Free all location lists | |
487 FOR_ALL_TAB_WINDOWS(tab, win) | |
488 qf_free_all(win); | |
489 } | |
490 # endif | |
491 | |
492 // Close all script inputs. | |
493 close_all_scripts(); | |
494 | |
495 if (curwin != NULL) | |
496 // Destroy all windows. Must come before freeing buffers. | |
497 win_free_all(); | |
498 | |
499 // Free all option values. Must come after closing windows. | |
500 free_all_options(); | |
501 | |
502 // Free all buffers. Reset 'autochdir' to avoid accessing things that | |
503 // were freed already. | |
504 # ifdef FEAT_AUTOCHDIR | |
505 p_acd = FALSE; | |
506 # endif | |
507 for (buf = firstbuf; buf != NULL; ) | |
508 { | |
509 bufref_T bufref; | |
510 | |
511 set_bufref(&bufref, buf); | |
512 nextbuf = buf->b_next; | |
513 close_buffer(NULL, buf, DOBUF_WIPE, FALSE, FALSE); | |
514 if (bufref_valid(&bufref)) | |
515 buf = nextbuf; // didn't work, try next one | |
516 else | |
517 buf = firstbuf; | |
518 } | |
519 | |
520 # ifdef FEAT_ARABIC | |
521 free_arshape_buf(); | |
522 # endif | |
523 | |
524 // Clear registers. | |
525 clear_registers(); | |
526 ResetRedobuff(); | |
527 ResetRedobuff(); | |
528 | |
529 # if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) | |
530 vim_free(serverDelayedStartName); | |
531 # endif | |
532 | |
533 // highlight info | |
534 free_highlight(); | |
535 | |
536 reset_last_sourcing(); | |
537 | |
538 if (first_tabpage != NULL) | |
539 { | |
540 free_tabpage(first_tabpage); | |
541 first_tabpage = NULL; | |
542 } | |
543 | |
544 # ifdef UNIX | |
545 // Machine-specific free. | |
546 mch_free_mem(); | |
547 # endif | |
548 | |
549 // message history | |
550 for (;;) | |
551 if (delete_first_msg() == FAIL) | |
552 break; | |
553 | |
554 # ifdef FEAT_JOB_CHANNEL | |
555 channel_free_all(); | |
556 # endif | |
557 # ifdef FEAT_TIMERS | |
558 timer_free_all(); | |
559 # endif | |
560 # ifdef FEAT_EVAL | |
561 // must be after channel_free_all() with unrefs partials | |
562 eval_clear(); | |
563 # endif | |
564 # ifdef FEAT_JOB_CHANNEL | |
565 // must be after eval_clear() with unrefs jobs | |
566 job_free_all(); | |
567 # endif | |
568 | |
569 free_termoptions(); | |
570 | |
571 // screenlines (can't display anything now!) | |
572 free_screenlines(); | |
573 | |
574 # if defined(FEAT_SOUND) | |
575 sound_free(); | |
576 # endif | |
577 # if defined(USE_XSMP) | |
578 xsmp_close(); | |
579 # endif | |
580 # ifdef FEAT_GUI_GTK | |
581 gui_mch_free_all(); | |
582 # endif | |
583 clear_hl_tables(); | |
584 | |
585 vim_free(IObuff); | |
586 vim_free(NameBuff); | |
587 # ifdef FEAT_QUICKFIX | |
588 check_quickfix_busy(); | |
589 # endif | |
590 } | |
591 #endif | |
592 | |
593 /* | |
594 * Copy "p[len]" into allocated memory, ignoring NUL characters. | |
595 * Returns NULL when out of memory. | |
596 */ | |
597 char_u * | |
598 vim_memsave(char_u *p, size_t len) | |
599 { | |
600 char_u *ret = alloc(len); | |
601 | |
602 if (ret != NULL) | |
603 mch_memmove(ret, p, len); | |
604 return ret; | |
605 } | |
606 | |
607 /* | |
608 * Replacement for free() that ignores NULL pointers. | |
609 * Also skip free() when exiting for sure, this helps when we caught a deadly | |
610 * signal that was caused by a crash in free(). | |
611 * If you want to set NULL after calling this function, you should use | |
612 * VIM_CLEAR() instead. | |
613 */ | |
614 void | |
615 vim_free(void *x) | |
616 { | |
617 if (x != NULL && !really_exiting) | |
618 { | |
619 #ifdef MEM_PROFILE | |
620 mem_pre_free(&x); | |
621 #endif | |
622 free(x); | |
623 } | |
624 } | |
625 | |
626 /************************************************************************ | |
627 * Functions for handling growing arrays. | |
628 */ | |
629 | |
630 /* | |
631 * Clear an allocated growing array. | |
632 */ | |
633 void | |
634 ga_clear(garray_T *gap) | |
635 { | |
636 vim_free(gap->ga_data); | |
637 ga_init(gap); | |
638 } | |
639 | |
640 /* | |
641 * Clear a growing array that contains a list of strings. | |
642 */ | |
643 void | |
644 ga_clear_strings(garray_T *gap) | |
645 { | |
646 int i; | |
647 | |
648 if (gap->ga_data != NULL) | |
649 for (i = 0; i < gap->ga_len; ++i) | |
650 vim_free(((char_u **)(gap->ga_data))[i]); | |
651 ga_clear(gap); | |
652 } | |
653 | |
654 /* | |
655 * Copy a growing array that contains a list of strings. | |
656 */ | |
657 int | |
658 ga_copy_strings(garray_T *from, garray_T *to) | |
659 { | |
660 int i; | |
661 | |
662 ga_init2(to, sizeof(char_u *), 1); | |
663 if (ga_grow(to, from->ga_len) == FAIL) | |
664 return FAIL; | |
665 | |
666 for (i = 0; i < from->ga_len; ++i) | |
667 { | |
668 char_u *orig = ((char_u **)from->ga_data)[i]; | |
669 char_u *copy; | |
670 | |
671 if (orig == NULL) | |
672 copy = NULL; | |
673 else | |
674 { | |
675 copy = vim_strsave(orig); | |
676 if (copy == NULL) | |
677 { | |
678 to->ga_len = i; | |
679 ga_clear_strings(to); | |
680 return FAIL; | |
681 } | |
682 } | |
683 ((char_u **)to->ga_data)[i] = copy; | |
684 } | |
685 to->ga_len = from->ga_len; | |
686 return OK; | |
687 } | |
688 | |
689 /* | |
690 * Initialize a growing array. Don't forget to set ga_itemsize and | |
691 * ga_growsize! Or use ga_init2(). | |
692 */ | |
693 void | |
694 ga_init(garray_T *gap) | |
695 { | |
696 gap->ga_data = NULL; | |
697 gap->ga_maxlen = 0; | |
698 gap->ga_len = 0; | |
699 } | |
700 | |
701 void | |
702 ga_init2(garray_T *gap, int itemsize, int growsize) | |
703 { | |
704 ga_init(gap); | |
705 gap->ga_itemsize = itemsize; | |
706 gap->ga_growsize = growsize; | |
707 } | |
708 | |
709 /* | |
710 * Make room in growing array "gap" for at least "n" items. | |
711 * Return FAIL for failure, OK otherwise. | |
712 */ | |
713 int | |
714 ga_grow(garray_T *gap, int n) | |
715 { | |
716 if (gap->ga_maxlen - gap->ga_len < n) | |
717 return ga_grow_inner(gap, n); | |
718 return OK; | |
719 } | |
720 | |
721 int | |
722 ga_grow_inner(garray_T *gap, int n) | |
723 { | |
724 size_t old_len; | |
725 size_t new_len; | |
726 char_u *pp; | |
727 | |
728 if (n < gap->ga_growsize) | |
729 n = gap->ga_growsize; | |
730 | |
731 // A linear growth is very inefficient when the array grows big. This | |
732 // is a compromise between allocating memory that won't be used and too | |
733 // many copy operations. A factor of 1.5 seems reasonable. | |
734 if (n < gap->ga_len / 2) | |
735 n = gap->ga_len / 2; | |
736 | |
737 new_len = gap->ga_itemsize * (gap->ga_len + n); | |
738 pp = vim_realloc(gap->ga_data, new_len); | |
739 if (pp == NULL) | |
740 return FAIL; | |
741 old_len = gap->ga_itemsize * gap->ga_maxlen; | |
742 vim_memset(pp + old_len, 0, new_len - old_len); | |
743 gap->ga_maxlen = gap->ga_len + n; | |
744 gap->ga_data = pp; | |
745 return OK; | |
746 } | |
747 | |
748 /* | |
749 * For a growing array that contains a list of strings: concatenate all the | |
750 * strings with a separating "sep". | |
751 * Returns NULL when out of memory. | |
752 */ | |
753 char_u * | |
754 ga_concat_strings(garray_T *gap, char *sep) | |
755 { | |
756 int i; | |
757 int len = 0; | |
758 int sep_len = (int)STRLEN(sep); | |
759 char_u *s; | |
760 char_u *p; | |
761 | |
762 for (i = 0; i < gap->ga_len; ++i) | |
763 len += (int)STRLEN(((char_u **)(gap->ga_data))[i]) + sep_len; | |
764 | |
765 s = alloc(len + 1); | |
766 if (s != NULL) | |
767 { | |
768 *s = NUL; | |
769 p = s; | |
770 for (i = 0; i < gap->ga_len; ++i) | |
771 { | |
772 if (p != s) | |
773 { | |
774 STRCPY(p, sep); | |
775 p += sep_len; | |
776 } | |
777 STRCPY(p, ((char_u **)(gap->ga_data))[i]); | |
778 p += STRLEN(p); | |
779 } | |
780 } | |
781 return s; | |
782 } | |
783 | |
784 /* | |
785 * Make a copy of string "p" and add it to "gap". | |
786 * When out of memory nothing changes and FAIL is returned. | |
787 */ | |
788 int | |
789 ga_add_string(garray_T *gap, char_u *p) | |
790 { | |
791 char_u *cp = vim_strsave(p); | |
792 | |
793 if (cp == NULL) | |
794 return FAIL; | |
795 | |
796 if (ga_grow(gap, 1) == FAIL) | |
797 { | |
798 vim_free(cp); | |
799 return FAIL; | |
800 } | |
801 ((char_u **)(gap->ga_data))[gap->ga_len++] = cp; | |
802 return OK; | |
803 } | |
804 | |
805 /* | |
806 * Concatenate a string to a growarray which contains bytes. | |
807 * When "s" is NULL does not do anything. | |
808 * Note: Does NOT copy the NUL at the end! | |
809 */ | |
810 void | |
811 ga_concat(garray_T *gap, char_u *s) | |
812 { | |
813 int len; | |
814 | |
815 if (s == NULL || *s == NUL) | |
816 return; | |
817 len = (int)STRLEN(s); | |
818 if (ga_grow(gap, len) == OK) | |
819 { | |
820 mch_memmove((char *)gap->ga_data + gap->ga_len, s, (size_t)len); | |
821 gap->ga_len += len; | |
822 } | |
823 } | |
824 | |
825 /* | |
826 * Concatenate 'len' bytes from string 's' to a growarray. | |
827 * When "s" is NULL does not do anything. | |
828 */ | |
829 void | |
830 ga_concat_len(garray_T *gap, char_u *s, size_t len) | |
831 { | |
832 if (s == NULL || *s == NUL) | |
833 return; | |
834 if (ga_grow(gap, (int)len) == OK) | |
835 { | |
836 mch_memmove((char *)gap->ga_data + gap->ga_len, s, len); | |
837 gap->ga_len += (int)len; | |
838 } | |
839 } | |
840 | |
841 /* | |
842 * Append one byte to a growarray which contains bytes. | |
843 */ | |
844 void | |
845 ga_append(garray_T *gap, int c) | |
846 { | |
847 if (ga_grow(gap, 1) == OK) | |
848 { | |
849 *((char *)gap->ga_data + gap->ga_len) = c; | |
850 ++gap->ga_len; | |
851 } | |
852 } | |
853 | |
854 #if (defined(UNIX) && !defined(USE_SYSTEM)) || defined(MSWIN) \ | |
855 || defined(PROTO) | |
856 /* | |
857 * Append the text in "gap" below the cursor line and clear "gap". | |
858 */ | |
859 void | |
860 append_ga_line(garray_T *gap) | |
861 { | |
862 // Remove trailing CR. | |
863 if (gap->ga_len > 0 | |
864 && !curbuf->b_p_bin | |
865 && ((char_u *)gap->ga_data)[gap->ga_len - 1] == CAR) | |
866 --gap->ga_len; | |
867 ga_append(gap, NUL); | |
868 ml_append(curwin->w_cursor.lnum++, gap->ga_data, 0, FALSE); | |
869 gap->ga_len = 0; | |
870 } | |
871 #endif | |
872 |