diff options
author | Yegappan Lakshmanan <yegappan@yahoo.com> | 2021-08-06 21:51:55 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-08-06 21:51:55 +0200 |
commit | cbae5802832b29f3a1af4cb6b0fc8cf69f17cbf4 (patch) | |
tree | ca9812e45634ddce3042e0723892cae596c6e62c | |
parent | 11328bc7df0ecc47f4025a10bb86882a659e9994 (diff) |
patch 8.2.3301: memory allocation functions don't have their own placev8.2.3301
Problem: Memory allocation functions don't have their own place.
Solution: Move memory allocation functions to alloc.c. (Yegappan
Lakshmanan, closes #8717)
-rw-r--r-- | Filelist | 2 | ||||
-rw-r--r-- | src/Make_ami.mak | 1 | ||||
-rw-r--r-- | src/Make_cyg_ming.mak | 1 | ||||
-rw-r--r-- | src/Make_mvc.mak | 4 | ||||
-rw-r--r-- | src/Make_vms.mms | 5 | ||||
-rw-r--r-- | src/Makefile | 10 | ||||
-rw-r--r-- | src/README.md | 1 | ||||
-rw-r--r-- | src/alloc.c | 872 | ||||
-rw-r--r-- | src/misc2.c | 866 | ||||
-rw-r--r-- | src/proto.h | 1 | ||||
-rw-r--r-- | src/proto/alloc.pro | 29 | ||||
-rw-r--r-- | src/proto/misc2.pro | 28 | ||||
-rw-r--r-- | src/version.c | 2 |
13 files changed, 938 insertions, 884 deletions
@@ -23,6 +23,7 @@ SRC_ALL = \ ci/setup-xvfb.sh \ src/Make_all.mak \ src/README.md \ + src/alloc.c \ src/alloc.h \ src/arabic.c \ src/arglist.c \ @@ -210,6 +211,7 @@ SRC_ALL = \ src/testdir/popupbounce.vim \ src/proto.h \ src/protodef.h \ + src/proto/alloc.pro \ src/proto/arabic.pro \ src/proto/arglist.pro \ src/proto/autocmd.pro \ diff --git a/src/Make_ami.mak b/src/Make_ami.mak index 678f99994b..12f7fe5571 100644 --- a/src/Make_ami.mak +++ b/src/Make_ami.mak @@ -80,6 +80,7 @@ endif # Common sources SRC += \ + alloc.c \ arabic.c \ arglist.c \ autocmd.c \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index f12ffac8e0..1b881b5f48 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -723,6 +723,7 @@ LIB = -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomdlg32 -lcomctl32 -lnetapi32 -l GUIOBJ = $(OUTDIR)/gui.o $(OUTDIR)/gui_w32.o $(OUTDIR)/gui_beval.o CUIOBJ = $(OUTDIR)/iscygpty.o OBJ = \ + $(OUTDIR)/alloc.o \ $(OUTDIR)/arabic.o \ $(OUTDIR)/arglist.o \ $(OUTDIR)/autocmd.o \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 18b5a88c5f..5d725cc056 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -733,6 +733,7 @@ INCL = vim.h alloc.h ascii.h ex_cmds.h feature.h errors.h globals.h \ spell.h structs.h term.h beval.h $(NBDEBUG_INCL) OBJ = \ + $(OUTDIR)\alloc.obj \ $(OUTDIR)\arabic.obj \ $(OUTDIR)\arglist.obj \ $(OUTDIR)\autocmd.obj \ @@ -1542,6 +1543,8 @@ test_vim9: .cpp{$(OUTDIR)/}.obj:: $(CC) $(CFLAGS_OUTDIR) $< +$(OUTDIR)/alloc.obj: $(OUTDIR) alloc.c $(INCL) + $(OUTDIR)/arabic.obj: $(OUTDIR) arabic.c $(INCL) $(OUTDIR)/arglist.obj: $(OUTDIR) arglist.c $(INCL) @@ -1932,6 +1935,7 @@ $(PATHDEF_SRC): Make_mvc.mak # End Custom Build proto.h: \ + proto/alloc.pro \ proto/arabic.pro \ proto/arglist.pro \ proto/autocmd.pro \ diff --git a/src/Make_vms.mms b/src/Make_vms.mms index 3f65201c50..9dde7616ee 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -306,6 +306,7 @@ ALL_LIBS = $(LIBS) $(GUI_LIB_DIR) $(GUI_LIB) $(XPM_LIB)\ $(PERL_LIB) $(PYTHON_LIB) $(TCL_LIB) $(RUBY_LIB) $(LUA_LIB) SRC = \ + alloc.c \ arabic.c \ arglist.c \ autocmd.c \ @@ -425,6 +426,7 @@ SRC = \ $(XDIFF_SRC) OBJ = \ + alloc.obj \ arabic.obj \ arglist.obj \ autocmd.obj \ @@ -738,6 +740,9 @@ lua_env : -@ ! .ENDIF +alloc.obj : alloc.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h errors.h globals.h arabic.obj : arabic.c vim.h arglist.obj : arglist.c vim.h [.auto]config.h feature.h os_unix.h autocmd.obj : autocmd.c vim.h [.auto]config.h feature.h os_unix.h diff --git a/src/Makefile b/src/Makefile index 6fa9d25c61..4ac2634a5e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1590,6 +1590,7 @@ include testdir/Make_all.mak # ALL_SRC: source files used for make depend and make lint BASIC_SRC = \ + alloc.c \ arabic.c \ arglist.c \ autocmd.c \ @@ -1747,6 +1748,7 @@ LINT_SRC = $(BASIC_SRC) $(GUI_SRC) \ #LINT_SRC = $(BASIC_SRC) OBJ_COMMON = \ + objects/alloc.o \ objects/arabic.o \ objects/arglist.o \ objects/autocmd.o \ @@ -1917,6 +1919,7 @@ ALL_OBJ = $(OBJ_COMMON) \ PRO_AUTO = \ + alloc.pro \ arabic.pro \ arglist.pro \ autocmd.pro \ @@ -3150,6 +3153,9 @@ objects/.dirstamp: # time. $(ALL_OBJ): objects/.dirstamp +objects/alloc.o: alloc.c + $(CCC) -o $@ alloc.c + objects/arabic.o: arabic.c $(CCC) -o $@ arabic.c @@ -3711,6 +3717,10 @@ installglinks_haiku: $(HAIKU_GLINKS) install_haiku_extra ############################################################################### ### (automatically generated by 'make depend') ### Dependencies: +objects/alloc.o: alloc.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h errors.h globals.h objects/arabic.o: arabic.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ diff --git a/src/README.md b/src/README.md index a3a23be2d6..cefbecaef0 100644 --- a/src/README.md +++ b/src/README.md @@ -23,6 +23,7 @@ Most code can be found in a file with an obvious name (incomplete list): File name | Description --------------- | ----------- +alloc.c | memory management arglist.c | handling argument list autocmd.c | autocommands blob.c | blob data type diff --git a/src/alloc.c b/src/alloc.c new file mode 100644 index 0000000000..817e322fd4 --- /dev/null +++ b/src/alloc.c @@ -0,0 +1,872 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * alloc.c: functions for memory management + */ + +#include "vim.h" + +/********************************************************************** + * Various routines dealing with allocation and deallocation of memory. + */ + +#if defined(MEM_PROFILE) || defined(PROTO) + +# define MEM_SIZES 8200 +static long_u mem_allocs[MEM_SIZES]; +static long_u mem_frees[MEM_SIZES]; +static long_u mem_allocated; +static long_u mem_freed; +static long_u mem_peak; +static long_u num_alloc; +static long_u num_freed; + + static void +mem_pre_alloc_s(size_t *sizep) +{ + *sizep += sizeof(size_t); +} + + static void +mem_pre_alloc_l(size_t *sizep) +{ + *sizep += sizeof(size_t); +} + + static void +mem_post_alloc( + void **pp, + size_t size) +{ + if (*pp == NULL) + return; + size -= sizeof(size_t); + *(long_u *)*pp = size; + if (size <= MEM_SIZES-1) + mem_allocs[size-1]++; + else + mem_allocs[MEM_SIZES-1]++; + mem_allocated += size; + if (mem_allocated - mem_freed > mem_peak) + mem_peak = mem_allocated - mem_freed; + num_alloc++; + *pp = (void *)((char *)*pp + sizeof(size_t)); +} + + static void +mem_pre_free(void **pp) +{ + long_u size; + + *pp = (void *)((char *)*pp - sizeof(size_t)); + size = *(size_t *)*pp; + if (size <= MEM_SIZES-1) + mem_frees[size-1]++; + else + mem_frees[MEM_SIZES-1]++; + mem_freed += size; + num_freed++; +} + +/* + * called on exit via atexit() + */ + void +vim_mem_profile_dump(void) +{ + int i, j; + + printf("\r\n"); + j = 0; + for (i = 0; i < MEM_SIZES - 1; i++) + { + if (mem_allocs[i] || mem_frees[i]) + { + if (mem_frees[i] > mem_allocs[i]) + printf("\r\n%s", _("ERROR: ")); + printf("[%4d / %4lu-%-4lu] ", i + 1, mem_allocs[i], mem_frees[i]); + j++; + if (j > 3) + { + j = 0; + printf("\r\n"); + } + } + } + + i = MEM_SIZES - 1; + if (mem_allocs[i]) + { + printf("\r\n"); + if (mem_frees[i] > mem_allocs[i]) + puts(_("ERROR: ")); + printf("[>%d / %4lu-%-4lu]", i, mem_allocs[i], mem_frees[i]); + } + + printf(_("\n[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n"), + mem_allocated, mem_freed, mem_allocated - mem_freed, mem_peak); + printf(_("[calls] total re/malloc()'s %lu, total free()'s %lu\n\n"), + num_alloc, num_freed); +} + +#endif // MEM_PROFILE + +#ifdef FEAT_EVAL + int +alloc_does_fail(size_t size) +{ + if (alloc_fail_countdown == 0) + { + if (--alloc_fail_repeat <= 0) + alloc_fail_id = 0; + do_outofmem_msg(size); + return TRUE; + } + --alloc_fail_countdown; + return FALSE; +} +#endif + +/* + * Some memory is reserved for error messages and for being able to + * call mf_release_all(), which needs some memory for mf_trans_add(). + */ +#define KEEP_ROOM (2 * 8192L) +#define KEEP_ROOM_KB (KEEP_ROOM / 1024L) + +/* + * The normal way to allocate memory. This handles an out-of-memory situation + * as well as possible, still returns NULL when we're completely out. + */ + void * +alloc(size_t size) +{ + return lalloc(size, TRUE); +} + +/* + * alloc() with an ID for alloc_fail(). + */ + void * +alloc_id(size_t size, alloc_id_T id UNUSED) +{ +#ifdef FEAT_EVAL + if (alloc_fail_id == id && alloc_does_fail(size)) + return NULL; +#endif + return lalloc(size, TRUE); +} + +/* + * Allocate memory and set all bytes to zero. + */ + void * +alloc_clear(size_t size) +{ + void *p; + + p = lalloc(size, TRUE); + if (p != NULL) + (void)vim_memset(p, 0, size); + return p; +} + +/* + * Same as alloc_clear() but with allocation id for testing + */ + void * +alloc_clear_id(size_t size, alloc_id_T id UNUSED) +{ +#ifdef FEAT_EVAL + if (alloc_fail_id == id && alloc_does_fail(size)) + return NULL; +#endif + return alloc_clear(size); +} + +/* + * Allocate memory like lalloc() and set all bytes to zero. + */ + void * +lalloc_clear(size_t size, int message) +{ + void *p; + + p = lalloc(size, message); + if (p != NULL) + (void)vim_memset(p, 0, size); + return p; +} + +/* + * Low level memory allocation function. + * This is used often, KEEP IT FAST! + */ + void * +lalloc(size_t size, int message) +{ + void *p; // pointer to new storage space + static int releasing = FALSE; // don't do mf_release_all() recursive + int try_again; +#if defined(HAVE_AVAIL_MEM) + static size_t allocated = 0; // allocated since last avail check +#endif + + // Safety check for allocating zero bytes + if (size == 0) + { + // Don't hide this message + emsg_silent = 0; + iemsg(_("E341: Internal error: lalloc(0, )")); + return NULL; + } + +#ifdef MEM_PROFILE + mem_pre_alloc_l(&size); +#endif + + /* + * Loop when out of memory: Try to release some memfile blocks and + * if some blocks are released call malloc again. + */ + for (;;) + { + /* + * Handle three kind of systems: + * 1. No check for available memory: Just return. + * 2. Slow check for available memory: call mch_avail_mem() after + * allocating KEEP_ROOM amount of memory. + * 3. Strict check for available memory: call mch_avail_mem() + */ + if ((p = malloc(size)) != NULL) + { +#ifndef HAVE_AVAIL_MEM + // 1. No check for available memory: Just return. + goto theend; +#else + // 2. Slow check for available memory: call mch_avail_mem() after + // allocating (KEEP_ROOM / 2) amount of memory. + allocated += size; + if (allocated < KEEP_ROOM / 2) + goto theend; + allocated = 0; + + // 3. check for available memory: call mch_avail_mem() + if (mch_avail_mem(TRUE) < KEEP_ROOM_KB && !releasing) + { + free(p); // System is low... no go! + p = NULL; + } + else + goto theend; +#endif + } + /* + * Remember that mf_release_all() is being called to avoid an endless + * loop, because mf_release_all() may call alloc() recursively. + */ + if (releasing) + break; + releasing = TRUE; + + clear_sb_text(TRUE); // free any scrollback text + try_again = mf_release_all(); // release as many blocks as possible + + releasing = FALSE; + if (!try_again) + break; + } + + if (message && p == NULL) + do_outofmem_msg(size); + +theend: +#ifdef MEM_PROFILE + mem_post_alloc(&p, size); +#endif + return p; +} + +/* + * lalloc() with an ID for alloc_fail(). + */ +#if defined(FEAT_SIGNS) || defined(PROTO) + void * +lalloc_id(size_t size, int message, alloc_id_T id UNUSED) +{ +#ifdef FEAT_EVAL + if (alloc_fail_id == id && alloc_does_fail(size)) + return NULL; +#endif + return (lalloc(size, message)); +} +#endif + +#if defined(MEM_PROFILE) || defined(PROTO) +/* + * realloc() with memory profiling. + */ + void * +mem_realloc(void *ptr, size_t size) +{ + void *p; + + mem_pre_free(&ptr); + mem_pre_alloc_s(&size); + + p = realloc(ptr, size); + + mem_post_alloc(&p, size); + + return p; +} +#endif + +/* +* Avoid repeating the error message many times (they take 1 second each). +* Did_outofmem_msg is reset when a character is read. +*/ + void +do_outofmem_msg(size_t size) +{ + if (!did_outofmem_msg) + { + // Don't hide this message + emsg_silent = 0; + + // Must come first to avoid coming back here when printing the error + // message fails, e.g. when setting v:errmsg. + did_outofmem_msg = TRUE; + + semsg(_("E342: Out of memory! (allocating %lu bytes)"), (long_u)size); + + if (starting == NO_SCREEN) + // Not even finished with initializations and already out of + // memory? Then nothing is going to work, exit. + mch_exit(123); + } +} + +#if defined(EXITFREE) || defined(PROTO) + +/* + * Free everything that we allocated. + * Can be used to detect memory leaks, e.g., with ccmalloc. + * NOTE: This is tricky! Things are freed that functions depend on. Don't be + * surprised if Vim crashes... + * Some things can't be freed, esp. things local to a library function. + */ + void +free_all_mem(void) +{ + buf_T *buf, *nextbuf; + + // When we cause a crash here it is caught and Vim tries to exit cleanly. + // Don't try freeing everything again. + if (entered_free_all_mem) + return; + entered_free_all_mem = TRUE; + // Don't want to trigger autocommands from here on. + block_autocmds(); + + // Close all tabs and windows. Reset 'equalalways' to avoid redraws. + p_ea = FALSE; + if (first_tabpage != NULL && first_tabpage->tp_next != NULL) + do_cmdline_cmd((char_u *)"tabonly!"); + if (!ONE_WINDOW) + do_cmdline_cmd((char_u *)"only!"); + +# if defined(FEAT_SPELL) + // Free all spell info. + spell_free_all(); +# endif + +# if defined(FEAT_BEVAL_TERM) + ui_remove_balloon(); +# endif +# ifdef FEAT_PROP_POPUP + if (curwin != NULL) + close_all_popups(TRUE); +# endif + + // Clear user commands (before deleting buffers). + ex_comclear(NULL); + + // When exiting from mainerr_arg_missing curbuf has not been initialized, + // and not much else. + if (curbuf != NULL) + { +# ifdef FEAT_MENU + // Clear menus. + do_cmdline_cmd((char_u *)"aunmenu *"); +# ifdef FEAT_MULTI_LANG + do_cmdline_cmd((char_u *)"menutranslate clear"); +# endif +# endif + // Clear mappings, abbreviations, breakpoints. + do_cmdline_cmd((char_u *)"lmapclear"); + do_cmdline_cmd((char_u *)"xmapclear"); + do_cmdline_cmd((char_u *)"mapclear"); + do_cmdline_cmd((char_u *)"mapclear!"); + do_cmdline_cmd((char_u *)"abclear"); +# if defined(FEAT_EVAL) + do_cmdline_cmd((char_u *)"breakdel *"); +# endif +# if defined(FEAT_PROFILE) + do_cmdline_cmd((char_u *)"profdel *"); +# endif +# if defined(FEAT_KEYMAP) + do_cmdline_cmd((char_u *)"set keymap="); +# endif + } + +# ifdef FEAT_TITLE + free_titles(); +# endif +# if defined(FEAT_SEARCHPATH) + free_findfile(); +# endif + + // Obviously named calls. + free_all_autocmds(); + clear_termcodes(); + free_all_marks(); + alist_clear(&global_alist); + free_homedir(); + free_users(); + free_search_patterns(); + free_old_sub(); + free_last_insert(); + free_insexpand_stuff(); + free_prev_shellcmd(); + free_regexp_stuff(); + free_tag_stuff(); + free_cd_dir(); +# ifdef FEAT_SIGNS + free_signs(); +# endif +# ifdef FEAT_EVAL + set_expr_line(NULL, NULL); +# endif +# ifdef FEAT_DIFF + if (curtab != NULL) + diff_clear(curtab); +# endif + clear_sb_text(TRUE); // free any scrollback text + + // Free some global vars. + free_username(); +# ifdef FEAT_CLIPBOARD + vim_regfree(clip_exclude_prog); +# endif + vim_free(last_cmdline); + vim_free(new_last_cmdline); + set_keep_msg(NULL, 0); + + // Clear cmdline history. + p_hi = 0; + init_history(); +# ifdef FEAT_PROP_POPUP + clear_global_prop_types(); +# endif + +# ifdef FEAT_QUICKFIX + { + win_T *win; + tabpage_T *tab; + + qf_free_all(NULL); + // Free all location lists + FOR_ALL_TAB_WINDOWS(tab, win) + qf_free_all(win); + } +# endif + + // Close all script inputs. + close_all_scripts(); + + if (curwin != NULL) + // Destroy all windows. Must come before freeing buffers. + win_free_all(); + + // Free all option values. Must come after closing windows. + free_all_options(); + + // Free all buffers. Reset 'autochdir' to avoid accessing things that + // were freed already. +# ifdef FEAT_AUTOCHDIR + p_acd = FALSE; +# endif + for (buf = firstbuf; buf != NULL; ) + { + bufref_T bufref; + + set_bufref(&bufref, buf); + nextbuf = buf->b_next; + close_buffer(NULL, buf, DOBUF_WIPE, FALSE, FALSE); + if (bufref_valid(&bufref)) + buf = nextbuf; // didn't work, try next one + else + buf = firstbuf; + } + +# ifdef FEAT_ARABIC + free_arshape_buf(); +# endif + + // Clear registers. + clear_registers(); + ResetRedobuff(); + ResetRedobuff(); + +# if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) + vim_free(serverDelayedStartName); +# endif + + // highlight info + free_highlight(); + + reset_last_sourcing(); + + if (first_tabpage != NULL) + { + free_tabpage(first_tabpage); + first_tabpage = NULL; + } + +# ifdef UNIX + // Machine-specific free. + mch_free_mem(); +# endif + + // message history + for (;;) + if (delete_first_msg() == FAIL) + break; + +# ifdef FEAT_JOB_CHANNEL + channel_free_all(); +# endif +# ifdef FEAT_TIMERS + timer_free_all(); +# endif +# ifdef FEAT_EVAL + // must be after channel_free_all() with unrefs partials + eval_clear(); +# endif +# ifdef FEAT_JOB_CHANNEL + // must be after eval_clear() with unrefs jobs + job_free_all(); +# endif + + free_termoptions(); + + // screenlines (can't display anything now!) + free_screenlines(); + +# if defined(FEAT_SOUND) + sound_free(); +# endif +# if defined(USE_XSMP) + xsmp_close(); +# endif +# ifdef FEAT_GUI_GTK + gui_mch_free_all(); +# endif + clear_hl_tables(); + + vim_free(IObuff); + vim_free(NameBuff); +# ifdef FEAT_QUICKFIX + check_quickfix_busy(); +# endif +} +#endif + +/* + * Copy "p[len]" into allocated memory, ignoring NUL characters. + * Returns NULL when out of memory. + */ + char_u * +vim_memsave(char_u *p, size_t len) +{ + char_u *ret = alloc(len); + + if (ret != NULL) + mch_memmove(ret, p, len); + return ret; +} + +/* + * Replacement for free() that ignores NULL pointers. + * Also skip free() when exiting for sure, this helps when we caught a deadly + * signal that was caused by a crash in free(). + * If you want to set NULL after calling this function, you should use + * VIM_CLEAR() instead. + */ + void +vim_free(void *x) +{ + if (x != NULL && !really_exiting) + { +#ifdef MEM_PROFILE + mem_pre_free(&x); +#endif + free(x); + } +} + +/************************************************************************ + * Functions for handling growing arrays. + */ + +/* + * Clear an allocated growing array. + */ + void +ga_clear(garray_T *gap) +{ + vim_free(gap->ga_data); + ga_init(gap); +} + +/* + * Clear a growing array that contains a list of strings. + */ + void +ga_clear_strings(garray_T *gap) +{ + int i; + + if (gap->ga_data != NULL) + for (i = 0; i < gap->ga_len; ++i) + vim_free(((char_u **)(gap->ga_data))[i]); + ga_clear(gap); +} + +/* + * Copy a growing array that contains a list of strings. + */ + int +ga_copy_strings(garray_T *from, garray_T *to) +{ + int i; + + ga_init2(to, sizeof(char_u *), 1); + if (ga_grow(to, from->ga_len) == FAIL) + return FAIL; + + for (i = 0; i < from->ga_len; ++i) + { + char_u *orig = ((char_u **)from->ga_data)[i]; + char_u *copy; + + if (orig == NULL) + copy = NULL; + else + { + copy = vim_strsave(orig); + if (copy == NULL) + { + to->ga_len = i; + ga_clear_strings(to); + return FAIL; + } + } + ((char_u **)to->ga_data)[i] = copy; + } + to->ga_len = from->ga_len; + return OK; +} + +/* + * Initialize a growing array. Don't forget to set ga_itemsize and + * ga_growsize! Or use ga_init2(). + */ + void +ga_init(garray_T *gap) +{ + gap->ga_data = NULL; + gap->ga_maxlen = 0; + gap->ga_len = 0; +} + + void +ga_init2(garray_T *gap, int itemsize, int growsize) +{ + ga_init(gap); + gap->ga_itemsize = itemsize; + gap->ga_growsize = growsize; +} + +/* + * Make room in growing array "gap" for at least "n" items. + * Return FAIL for failure, OK otherwise. + */ + int +ga_grow(garray_T *gap, int n) +{ + if (gap->ga_maxlen - gap->ga_len < n) + return ga_grow_inner(gap, n); + return OK; +} + + int +ga_grow_inner(garray_T *gap, int n) +{ + size_t old_len; + size_t new_len; + char_u *pp; + + if (n < gap->ga_growsize) + n = gap->ga_growsize; + + // A linear growth is very inefficient when the array grows big. This + // is a compromise between allocating memory that won't be used and too + // many copy operations. A factor of 1.5 seems reasonable. + if (n < gap->ga_len / 2) + n = gap->ga_len / 2; + + new_len = gap->ga_itemsize * (gap->ga_len + n); + pp = vim_realloc(gap->ga_data, new_len); + if (pp == NULL) + return FAIL; + old_len = gap->ga_itemsize * gap->ga_maxlen; + vim_memset(pp + old_len, 0, new_len - old_len); + gap->ga_maxlen = gap->ga_len + n; + gap->ga_data = pp; + return OK; +} + +/* + * For a growing array that contains a list of strings: concatenate all the + * strings with a separating "sep". + * Returns NULL when out of memory. + */ + char_u * +ga_concat_strings(garray_T *gap, char *sep) +{ + int i; + int len = 0; + int sep_len = (int)STRLEN(sep); + char_u *s; + char_u *p; + + for (i = 0; i < gap->ga_len; ++i) + len += (int)STRLEN(((char_u **)(gap->ga_data))[i]) + sep_len; + + s = alloc(len + 1); + if (s != NULL) + { + *s = NUL; + p = s; + for (i = 0; i < gap->ga_len; ++i) + { + if (p != s) + { + STRCPY(p, sep); + p += sep_len; + } + STRCPY(p, ((char_u **)(gap->ga_data))[i]); + p += STRLEN(p); + } + } + return s; +} + +/* + * Make a copy of string "p" and add it to "gap". + * When out of memory nothing changes and FAIL is returned. + */ + int +ga_add_string(garray_T *gap, char_u *p) +{ + char_u *cp = vim_strsave(p); + + if (cp == NULL) + return FAIL; + + if (ga_grow(gap, 1) == FAIL) + { + vim_free(cp); + return FAIL; + } + ((char_u **)(gap->ga_data))[gap->ga_len++] = cp; + return OK; +} + +/* + * Concatenate a string to a growarray which contains bytes. + * When "s" is NULL does not do anything. + * Note: Does NOT copy the NUL at the end! + */ + void +ga_concat(garray_T *gap, char_u *s) +{ + int len; + + if (s == NULL || *s == NUL) + return; + len = (int)STRLEN(s); + if (ga_grow(gap, len) == OK) + { + mch_memmove((char *)gap->ga_data + gap->ga_len, s, (size_t)len); + gap->ga_len += len; + } +} + +/* + * Concatenate 'len' bytes from string 's' to a growarray. + * When "s" is NULL does not do anything. + */ + void +ga_concat_len(garray_T *gap, char_u *s, size_t len) +{ + if (s == NULL || *s == NUL) + return; + if (ga_grow(gap, (int)len) == OK) + { + mch_memmove((char *)gap->ga_data + gap->ga_len, s, len); + gap->ga_len += (int)len; + } +} + +/* + * Append one byte to a growarray which contains bytes. + */ + void +ga_append(garray_T *gap, int c) +{ + if (ga_grow(gap, 1) == OK) + { + *((char *)gap->ga_data + gap->ga_len) = c; + ++gap->ga_len; + } +} + +#if (defined(UNIX) && !defined(USE_SYSTEM)) || defined(MSWIN) \ + || defined(PROTO) +/* + * Append the text in "gap" below the cursor line and clear "gap". + */ + void +append_ga_line(garray_T *gap) +{ + // Remove trailing CR. + if (gap->ga_len > 0 + && !curbuf->b_p_bin + && ((char_u *)gap->ga_data)[gap->ga_len - 1] == CAR) + --gap->ga_len; + ga_append(gap, NUL); + ml_append(curwin->w_cursor.lnum++, gap->ga_data, 0, FALSE); + gap->ga_len = 0; +} +#endif + diff --git a/src/misc2.c b/src/misc2.c index 2e83d2dd17..271014f7a3 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -693,597 +693,6 @@ leftcol_changed(void) return retval; } -/********************************************************************** - * Various routines dealing with allocation and deallocation of memory. - */ - -#if defined(MEM_PROFILE) || defined(PROTO) - -# define MEM_SIZES 8200 -static long_u mem_allocs[MEM_SIZES]; -static long_u mem_frees[MEM_SIZES]; -static long_u mem_allocated; -static long_u mem_freed; -static long_u mem_peak; -static long_u num_alloc; -static long_u num_freed; - - static void -mem_pre_alloc_s(size_t *sizep) -{ - *sizep += sizeof(size_t); -} - - static void -mem_pre_alloc_l(size_t *sizep) -{ - *sizep += sizeof(size_t); -} - - static void -mem_post_alloc( - void **pp, - size_t size) -{ - if (*pp == NULL) - return; - size -= sizeof(size_t); - *(long_u *)*pp = size; - if (size <= MEM_SIZES-1) - mem_allocs[size-1]++; - else - mem_allocs[MEM_SIZES-1]++; - mem_allocated += size; - if (mem_allocated - mem_freed > mem_peak) - mem_peak = mem_allocated - mem_freed; - num_alloc++; - *pp = (void *)((char *)*pp + sizeof(size_t)); -} - - static void -mem_pre_free(void **pp) -{ - long_u size; - - *pp = (void *)((char *)*pp - sizeof(size_t)); - size = *(size_t *)*pp; - if (size <= MEM_SIZES-1) - mem_frees[size-1]++; - else - mem_frees[MEM_SIZES-1]++; - mem_freed += size; - num_freed++; -} - -/* - * called on exit via atexit() - */ - void -vim_mem_profile_dump(void) -{ - int i, j; - - printf("\r\n"); - j = 0; - for (i = 0; i < MEM_SIZES - 1; i++) - { - if (mem_allocs[i] || mem_frees[i]) - { - if (mem_frees[i] > mem_allocs[i]) - printf("\r\n%s", _("ERROR: ")); - printf("[%4d / %4lu-%-4lu] ", i + 1, mem_allocs[i], mem_frees[i]); - j++; - if (j > 3) - { - j = 0; - printf("\r\n"); - } - } - } - - i = MEM_SIZES - 1; - if (mem_allocs[i]) - { - printf("\r\n"); - if (mem_frees[i] > mem_allocs[i]) - puts(_("ERROR: ")); - printf("[>%d / %4lu-%-4lu]", i, mem_allocs[i], mem_frees[i]); - } - - printf(_("\n[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n"), - mem_allocated, mem_freed, mem_allocated - mem_freed, mem_peak); - printf(_("[calls] total re/malloc()'s %lu, total free()'s %lu\n\n"), - num_alloc, num_freed); -} - -#endif // MEM_PROFILE - -#ifdef FEAT_EVAL - int -alloc_does_fail(size_t size) -{ - if (alloc_fail_countdown == 0) - { - if (--alloc_fail_repeat <= 0) - alloc_fail_id = 0; - do_outofmem_msg(size); - return TRUE; - } - --alloc_fail_countdown; - return FALSE; -} -#endif - -/* - * Some memory is reserved for error messages and for being able to - * call mf_release_all(), which needs some memory for mf_trans_add(). - */ -#define KEEP_ROOM (2 * 8192L) -#define KEEP_ROOM_KB (KEEP_ROOM / 1024L) - |