/* * Translate any special characters in buf[bufsize] in-place. * The result is a string with only printable characters, but if there is not * enough room, not all characters will be translated. */ void trans_characters(char_u *buf, int bufsize) { int len; /* length of string needing translation */ int room; /* room in buffer after string */ char_u *trs; /* translated character */ int trs_len; /* length of trs[] */ len = (int)STRLEN(buf); room = bufsize - len; while (*buf != 0) { /* Assume a multi-byte character doesn't need translation. */ if (has_mbyte && (trs_len = (*mb_ptr2len)(buf)) > 1) len -= trs_len; else { trs = transchar_byte(*buf); trs_len = (int)STRLEN(trs); if (trs_len > 1) { room -= trs_len - 1; if (room <= 0) return; mch_memmove(buf + trs_len, buf + 1, (size_t)len); } mch_memmove(buf, trs, (size_t)trs_len); --len; } buf += trs_len; } }
/* * Add a new channel slot, return the index. * The channel isn't actually used into ch_fd is set >= 0; * Returns -1 if all channels are in use. */ static int add_channel(void) { int idx; channel_T *new_channels; if (channels != NULL) for (idx = 0; idx < channel_count; ++idx) if (channels[idx].ch_fd < 0) /* re-use a closed channel slot */ return idx; if (channel_count == MAX_OPEN_CHANNELS) return -1; new_channels = (channel_T *)alloc(sizeof(channel_T) * (channel_count + 1)); if (new_channels == NULL) return -1; if (channels != NULL) mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count); channels = new_channels; (void)vim_memset(&channels[channel_count], 0, sizeof(channel_T)); channels[channel_count].ch_fd = (sock_T)-1; #ifdef FEAT_GUI_X11 channels[channel_count].ch_inputHandler = (XtInputId)NULL; #endif #ifdef FEAT_GUI_GTK channels[channel_count].ch_inputHandler = 0; #endif #ifdef FEAT_GUI_W32 channels[channel_count].ch_inputHandler = -1; #endif return channel_count++; }
/* * Read the crypt method specific header data from "fp". * Return an allocated cryptstate_T or NULL on error. */ cryptstate_T * crypt_create_from_file(FILE *fp, char_u *key) { int method_nr; int header_len; char magic_buffer[CRYPT_MAGIC_LEN]; char_u *buffer; cryptstate_T *state; if (fread(magic_buffer, CRYPT_MAGIC_LEN, 1, fp) != 1) return NULL; method_nr = crypt_method_nr_from_magic(magic_buffer, CRYPT_MAGIC_LEN); if (method_nr < 0) return NULL; header_len = crypt_get_header_len(method_nr); if ((buffer = alloc(header_len)) == NULL) return NULL; mch_memmove(buffer, magic_buffer, CRYPT_MAGIC_LEN); if (header_len > CRYPT_MAGIC_LEN && fread(buffer + CRYPT_MAGIC_LEN, header_len - CRYPT_MAGIC_LEN, 1, fp) != 1) { vim_free(buffer); return NULL; } state = crypt_create_from_header(method_nr, key, buffer); vim_free(buffer); return state; }
/// Concatenate a string to a growarray which contains characters. /// Note: Does NOT copy the NUL at the end! /// /// @param gap /// @param s void ga_concat(garray_T *gap, char_u *s) { int 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; } }
static void * py_memsave(void *p, size_t len) { void *r; if (!(r = PyMem_Malloc(len))) return NULL; mch_memmove(r, p, len); return r; }
/* * Allocate a cryptstate_T for writing and initialize it with "key". * Allocates and fills in the header and stores it in "header", setting * "header_len". The header may include salt and seed, depending on * cryptmethod. Caller must free header. * Returns the state or NULL on failure. */ cryptstate_T * crypt_create_for_writing( int method_nr, char_u *key, char_u **header, int *header_len) { int len = crypt_get_header_len(method_nr); char_u *salt = NULL; char_u *seed = NULL; int salt_len = cryptmethods[method_nr].salt_len; int seed_len = cryptmethods[method_nr].seed_len; cryptstate_T *state; *header_len = len; *header = alloc(len); if (*header == NULL) return NULL; mch_memmove(*header, cryptmethods[method_nr].magic, CRYPT_MAGIC_LEN); if (salt_len > 0 || seed_len > 0) { if (salt_len > 0) salt = *header + CRYPT_MAGIC_LEN; if (seed_len > 0) seed = *header + CRYPT_MAGIC_LEN + salt_len; /* TODO: Should this be crypt method specific? (Probably not worth * it). sha2_seed is pretty bad for large amounts of entropy, so make * that into something which is suitable for anything. */ sha2_seed(salt, salt_len, seed, seed_len); } state = crypt_create(method_nr, key, salt, salt_len, seed, seed_len); if (state == NULL) { vim_free(*header); *header = NULL; } return state; }
/* * Add a new channel slot, return the index. * The channel isn't actually used into ch_fd is set >= 0; * Returns -1 if all channels are in use. */ static int add_channel(void) { int idx; channel_T *new_channels; channel_T *ch; if (channels != NULL) for (idx = 0; idx < channel_count; ++idx) if (channels[idx].ch_fd < 0) /* re-use a closed channel slot */ return idx; if (channel_count == MAX_OPEN_CHANNELS) return -1; new_channels = (channel_T *)alloc(sizeof(channel_T) * (channel_count + 1)); if (new_channels == NULL) return -1; if (channels != NULL) mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count); channels = new_channels; ch = &channels[channel_count]; (void)vim_memset(ch, 0, sizeof(channel_T)); ch->ch_fd = (sock_T)-1; #ifdef FEAT_GUI_X11 ch->ch_inputHandler = (XtInputId)NULL; #endif #ifdef FEAT_GUI_GTK ch->ch_inputHandler = 0; #endif #ifdef FEAT_GUI_W32 ch->ch_inputHandler = -1; #endif /* initialize circular queues */ ch->ch_head.next = &ch->ch_head; ch->ch_head.prev = &ch->ch_head; ch->ch_json_head.next = &ch->ch_json_head; ch->ch_json_head.prev = &ch->ch_json_head; return channel_count++; }
/* * Add a new channel slot, return the index. * Returns -1 if out of space. */ static int add_channel(void) { int idx; channel_T *new_channels; if (channels != NULL) for (idx = 0; idx < channel_count; ++idx) if (channels[idx].ch_fd < 0) /* re-use a closed channel slot */ return idx; if (channel_count == MAX_OPEN_CHANNELS) return -1; new_channels = (channel_T *)alloc(sizeof(channel_T) * channel_count + 1); if (new_channels == NULL) return -1; if (channels != NULL) mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count); channels = new_channels; channels[channel_count].ch_fd = (sock_T)-1; return channel_count++; }
static int json_decode_string(js_read_T *reader, typval_T *res, int quote) { garray_T ga; int len; char_u *p; int c; varnumber_T nr; if (res != NULL) ga_init2(&ga, 1, 200); p = reader->js_buf + reader->js_used + 1; /* skip over " or ' */ while (*p != quote) { /* The JSON is always expected to be utf-8, thus use utf functions * here. The string is converted below if needed. */ if (*p == NUL || p[1] == NUL #ifdef FEAT_MBYTE || utf_ptr2len(p) < utf_byte2len(*p) #endif ) { /* Not enough bytes to make a character or end of the string. Get * more if possible. */ if (reader->js_fill == NULL) break; len = (int)(reader->js_end - p); reader->js_used = (int)(p - reader->js_buf); if (!reader->js_fill(reader)) break; /* didn't get more */ p = reader->js_buf + reader->js_used; reader->js_end = reader->js_buf + STRLEN(reader->js_buf); continue; } if (*p == '\\') { c = -1; switch (p[1]) { case '\\': c = '\\'; break; case '"': c = '"'; break; case 'b': c = BS; break; case 't': c = TAB; break; case 'n': c = NL; break; case 'f': c = FF; break; case 'r': c = CAR; break; case 'u': if (reader->js_fill != NULL && (int)(reader->js_end - p) < NUMBUFLEN) { reader->js_used = (int)(p - reader->js_buf); if (reader->js_fill(reader)) { p = reader->js_buf + reader->js_used; reader->js_end = reader->js_buf + STRLEN(reader->js_buf); } } nr = 0; len = 0; vim_str2nr(p + 2, NULL, &len, STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4); p += len + 2; if (0xd800 <= nr && nr <= 0xdfff && (int)(reader->js_end - p) >= 6 && *p == '\\' && *(p+1) == 'u') { varnumber_T nr2 = 0; /* decode surrogate pair: \ud812\u3456 */ len = 0; vim_str2nr(p + 2, NULL, &len, STR2NR_HEX + STR2NR_FORCE, &nr2, NULL, 4); if (0xdc00 <= nr2 && nr2 <= 0xdfff) { p += len + 2; nr = (((nr - 0xd800) << 10) | ((nr2 - 0xdc00) & 0x3ff)) + 0x10000; } } if (res != NULL) { #ifdef FEAT_MBYTE char_u buf[NUMBUFLEN]; buf[utf_char2bytes((int)nr, buf)] = NUL; ga_concat(&ga, buf); #else ga_append(&ga, (int)nr); #endif } break; default: /* not a special char, skip over \ */ ++p; continue; } if (c > 0) { p += 2; if (res != NULL) ga_append(&ga, c); } } else { #ifdef FEAT_MBYTE len = utf_ptr2len(p); #else len = 1; #endif if (res != NULL) { if (ga_grow(&ga, len) == FAIL) { ga_clear(&ga); return FAIL; } mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len); ga.ga_len += len; } p += len; } } reader->js_used = (int)(p - reader->js_buf); if (*p == quote) { ++reader->js_used; if (res != NULL) { ga_append(&ga, NUL); res->v_type = VAR_STRING; #if defined(FEAT_MBYTE) && defined(USE_ICONV) if (!enc_utf8) { vimconv_T conv; /* Convert the utf-8 string to 'encoding'. */ conv.vc_type = CONV_NONE; convert_setup(&conv, (char_u*)"utf-8", p_enc); if (conv.vc_type != CONV_NONE) { res->vval.v_string = string_convert(&conv, ga.ga_data, NULL); vim_free(ga.ga_data); } convert_setup(&conv, NULL, NULL); } else #endif res->vval.v_string = ga.ga_data; } return OK; } if (res != NULL) { res->v_type = VAR_SPECIAL; res->vval.v_number = VVAL_NONE; ga_clear(&ga); } return MAYBE; }
/* * Convert the string "str[orglen]" to do ignore-case comparing. Uses the * current locale. * When "buf" is NULL returns an allocated string (NULL for out-of-memory). * Otherwise puts the result in "buf[buflen]". */ char_u *str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) { garray_T ga; int i; int len = orglen; #define GA_CHAR(i) ((char_u *)ga.ga_data)[i] #define GA_PTR(i) ((char_u *)ga.ga_data + i) #define STR_CHAR(i) (buf == NULL ? GA_CHAR(i) : buf[i]) #define STR_PTR(i) (buf == NULL ? GA_PTR(i) : buf + i) /* Copy "str" into "buf" or allocated memory, unmodified. */ if (buf == NULL) { ga_init2(&ga, 1, 10); if (ga_grow(&ga, len + 1) == FAIL) return NULL; mch_memmove(ga.ga_data, str, (size_t)len); ga.ga_len = len; } else { if (len >= buflen) /* Ugly! */ len = buflen - 1; mch_memmove(buf, str, (size_t)len); } if (buf == NULL) GA_CHAR(len) = NUL; else buf[len] = NUL; /* Make each character lower case. */ i = 0; while (STR_CHAR(i) != NUL) { if (enc_utf8 || (has_mbyte && MB_BYTE2LEN(STR_CHAR(i)) > 1)) { if (enc_utf8) { int c = utf_ptr2char(STR_PTR(i)); int olen = utf_ptr2len(STR_PTR(i)); int lc = utf_tolower(c); /* Only replace the character when it is not an invalid * sequence (ASCII character or more than one byte) and * utf_tolower() doesn't return the original character. */ if ((c < 0x80 || olen > 1) && c != lc) { int nlen = utf_char2len(lc); /* If the byte length changes need to shift the following * characters forward or backward. */ if (olen != nlen) { if (nlen > olen) { if (buf == NULL ? ga_grow(&ga, nlen - olen + 1) == FAIL : len + nlen - olen >= buflen) { /* out of memory, keep old char */ lc = c; nlen = olen; } } if (olen != nlen) { if (buf == NULL) { STRMOVE(GA_PTR(i) + nlen, GA_PTR(i) + olen); ga.ga_len += nlen - olen; } else { STRMOVE(buf + i + nlen, buf + i + olen); len += nlen - olen; } } } (void)utf_char2bytes(lc, STR_PTR(i)); } } /* skip to next multi-byte char */ i += (*mb_ptr2len)(STR_PTR(i)); } else { if (buf == NULL) GA_CHAR(i) = TOLOWER_LOC(GA_CHAR(i)); else buf[i] = TOLOWER_LOC(buf[i]); ++i; } } if (buf == NULL) return (char_u *)ga.ga_data; return buf; }
/* * Shrink a hashtable when there is too much empty space. * Grow a hashtable when there is not enough empty space. * Returns OK or FAIL (out of memory). */ static int hash_may_resize( hashtab_T *ht, int minitems) /* minimal number of items */ { hashitem_T temparray[HT_INIT_SIZE]; hashitem_T *oldarray, *newarray; hashitem_T *olditem, *newitem; unsigned newi; int todo; long_u oldsize, newsize; long_u minsize; long_u newmask; hash_T perturb; /* Don't resize a locked table. */ if (ht->ht_locked > 0) return OK; #ifdef HT_DEBUG if (ht->ht_used > ht->ht_filled) EMSG("hash_may_resize(): more used than filled"); if (ht->ht_filled >= ht->ht_mask + 1) EMSG("hash_may_resize(): table completely filled"); #endif if (minitems == 0) { /* Return quickly for small tables with at least two NULL items. NULL * items are required for the lookup to decide a key isn't there. */ if (ht->ht_filled < HT_INIT_SIZE - 1 && ht->ht_array == ht->ht_smallarray) return OK; /* * Grow or refill the array when it's more than 2/3 full (including * removed items, so that they get cleaned up). * Shrink the array when it's less than 1/5 full. When growing it is * at least 1/4 full (avoids repeated grow-shrink operations) */ oldsize = ht->ht_mask + 1; if (ht->ht_filled * 3 < oldsize * 2 && ht->ht_used > oldsize / 5) return OK; if (ht->ht_used > 1000) minsize = ht->ht_used * 2; /* it's big, don't make too much room */ else minsize = ht->ht_used * 4; /* make plenty of room */ } else { /* Use specified size. */ if ((long_u)minitems < ht->ht_used) /* just in case... */ minitems = (int)ht->ht_used; minsize = minitems * 3 / 2; /* array is up to 2/3 full */ } newsize = HT_INIT_SIZE; while (newsize < minsize) { newsize <<= 1; /* make sure it's always a power of 2 */ if (newsize == 0) return FAIL; /* overflow */ } if (newsize == HT_INIT_SIZE) { /* Use the small array inside the hashdict structure. */ newarray = ht->ht_smallarray; if (ht->ht_array == newarray) { /* Moving from ht_smallarray to ht_smallarray! Happens when there * are many removed items. Copy the items to be able to clean up * removed items. */ mch_memmove(temparray, newarray, sizeof(temparray)); oldarray = temparray; } else oldarray = ht->ht_array; } else { /* Allocate an array. */ newarray = (hashitem_T *)alloc((unsigned) (sizeof(hashitem_T) * newsize)); if (newarray == NULL) { /* Out of memory. When there are NULL items still return OK. * Otherwise set ht_error, because lookup may result in a hang if * we add another item. */ if (ht->ht_filled < ht->ht_mask) return OK; ht->ht_error = TRUE; return FAIL; } oldarray = ht->ht_array; } vim_memset(newarray, 0, (size_t)(sizeof(hashitem_T) * newsize)); /* * Move all the items from the old array to the new one, placing them in * the right spot. The new array won't have any removed items, thus this * is also a cleanup action. */ newmask = newsize - 1; todo = (int)ht->ht_used; for (olditem = oldarray; todo > 0; ++olditem) if (!HASHITEM_EMPTY(olditem)) { /* * The algorithm to find the spot to add the item is identical to * the algorithm to find an item in hash_lookup(). But we only * need to search for a NULL key, thus it's simpler. */ newi = (unsigned)(olditem->hi_hash & newmask); newitem = &newarray[newi]; if (newitem->hi_key != NULL) for (perturb = olditem->hi_hash; ; perturb >>= PERTURB_SHIFT) { newi = (unsigned)((newi << 2U) + newi + perturb + 1U); newitem = &newarray[newi & newmask]; if (newitem->hi_key == NULL) break; } *newitem = *olditem; --todo; }
/* * Handle marks in the viminfo file: * fp_out != NULL: copy marks for buffers not in buffer list * fp_out == NULL && (flags & VIF_WANT_MARKS): read marks for curbuf only * fp_out == NULL && (flags & VIF_GET_OLDFILES | VIF_FORCEIT): fill v:oldfiles */ void copy_viminfo_marks(vir_T *virp, FILE *fp_out, int count, int eof, int flags) { char_u *line = virp->vir_line; buf_T *buf; int num_marked_files; int load_marks; int copy_marks_out; char_u *str; int i; char_u *p; char_u *name_buf; pos_T pos; list_T *list = NULL; if ((name_buf = alloc(LSIZE)) == NULL) return; *name_buf = NUL; if (fp_out == NULL && (flags & (VIF_GET_OLDFILES | VIF_FORCEIT))) { list = list_alloc(); if (list != NULL) set_vim_var_list(VV_OLDFILES, list); } num_marked_files = get_viminfo_parameter('\''); while (!eof && (count < num_marked_files || fp_out == NULL)) { if (line[0] != '>') { if (line[0] != '\n' && line[0] != '\r' && line[0] != '#') { if (viminfo_error("E576: ", _("Missing '>'"), line)) break; /* too many errors, return now */ } eof = vim_fgets(line, LSIZE, virp->vir_fd); continue; /* Skip this dud line */ } /* * Handle long line and translate escaped characters. * Find file name, set str to start. * Ignore leading and trailing white space. */ str = skipwhite(line + 1); str = viminfo_readstring(virp, (int)(str - virp->vir_line), FALSE); if (str == NULL) continue; p = str + STRLEN(str); while (p != str && (*p == NUL || vim_isspace(*p))) p--; if (*p) p++; *p = NUL; if (list != NULL) list_append_string(list, str, -1); /* * If fp_out == NULL, load marks for current buffer. * If fp_out != NULL, copy marks for buffers not in buflist. */ load_marks = copy_marks_out = FALSE; if (fp_out == NULL) { if ((flags & VIF_WANT_MARKS) && curbuf->b_ffname != NULL) { if (*name_buf == NUL) /* only need to do this once */ home_replace(NULL, curbuf->b_ffname, name_buf, LSIZE, TRUE); if (fnamecmp(str, name_buf) == 0) load_marks = TRUE; } } else { /* fp_out != NULL */ /* This is slow if there are many buffers!! */ for (buf = firstbuf; buf != NULL; buf = buf->b_next) if (buf->b_ffname != NULL) { home_replace(NULL, buf->b_ffname, name_buf, LSIZE, TRUE); if (fnamecmp(str, name_buf) == 0) break; } /* * copy marks if the buffer has not been loaded */ if (buf == NULL || !buf->b_marks_read) { copy_marks_out = TRUE; fputs("\n> ", fp_out); viminfo_writestring(fp_out, str); count++; } } vim_free(str); pos.coladd = 0; while (!(eof = viminfo_readline(virp)) && line[0] == TAB) { if (load_marks) { if (line[1] != NUL) { unsigned u; sscanf((char *)line + 2, "%ld %u", &pos.lnum, &u); pos.col = u; switch (line[1]) { case '"': curbuf->b_last_cursor = pos; break; case '^': curbuf->b_last_insert = pos; break; case '.': curbuf->b_last_change = pos; break; case '+': /* changelist positions are stored oldest * first */ if (curbuf->b_changelistlen == JUMPLISTSIZE) /* list is full, remove oldest entry */ mch_memmove(curbuf->b_changelist, curbuf->b_changelist + 1, sizeof(pos_T) * (JUMPLISTSIZE - 1)); else ++curbuf->b_changelistlen; curbuf->b_changelist[ curbuf->b_changelistlen - 1] = pos; break; default: if ((i = line[1] - 'a') >= 0 && i < NMARKS) curbuf->b_namedm[i] = pos; } } } else if (copy_marks_out) fputs((char *)line, fp_out); } if (load_marks) { win_T *wp; FOR_ALL_WINDOWS(wp) { if (wp->w_buffer == curbuf) wp->w_changelistidx = curbuf->b_changelistlen; } break; } } vim_free(name_buf); }
static int json_decode_string(js_read_T *reader, typval_T *res) { garray_T ga; int len; char_u *p; int c; long nr; char_u buf[NUMBUFLEN]; if (res != NULL) ga_init2(&ga, 1, 200); p = reader->js_buf + reader->js_used + 1; /* skip over " */ while (*p != '"') { if (*p == NUL || p[1] == NUL #ifdef FEAT_MBYTE || utf_ptr2len(p) < utf_byte2len(*p) #endif ) { if (reader->js_fill == NULL) break; len = (int)(reader->js_end - p); reader->js_used = (int)(p - reader->js_buf); if (!reader->js_fill(reader)) break; /* didn't get more */ p = reader->js_buf + reader->js_used; reader->js_end = reader->js_buf + STRLEN(reader->js_buf); continue; } if (*p == '\\') { c = -1; switch (p[1]) { case '\\': c = '\\'; break; case '"': c = '"'; break; case 'b': c = BS; break; case 't': c = TAB; break; case 'n': c = NL; break; case 'f': c = FF; break; case 'r': c = CAR; break; case 'u': if (reader->js_fill != NULL && (int)(reader->js_end - p) < NUMBUFLEN) { reader->js_used = (int)(p - reader->js_buf); if (reader->js_fill(reader)) { p = reader->js_buf + reader->js_used; reader->js_end = reader->js_buf + STRLEN(reader->js_buf); } } vim_str2nr(p + 2, NULL, &len, STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4); p += len + 2; if (res != NULL) { #ifdef FEAT_MBYTE buf[(*mb_char2bytes)((int)nr, buf)] = NUL; ga_concat(&ga, buf); #else ga_append(&ga, nr); #endif } break; default: /* not a special char, skip over \ */ ++p; continue; } if (c > 0) { p += 2; if (res != NULL) ga_append(&ga, c); } } else { len = MB_PTR2LEN(p); if (res != NULL) { if (ga_grow(&ga, len) == FAIL) { ga_clear(&ga); return FAIL; } mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len); ga.ga_len += len; } p += len; } } reader->js_used = (int)(p - reader->js_buf); if (*p == '"') { ++reader->js_used; if (res != NULL) { res->v_type = VAR_STRING; res->vval.v_string = ga.ga_data; } return OK; } if (res != NULL) { res->v_type = VAR_SPECIAL; res->vval.v_number = VVAL_NONE; ga_clear(&ga); } return MAYBE; }
/// Shrink a hashtable when there is too much empty space. /// Grow a hashtable when there is not enough empty space. /// /// @param ht /// @param minitems minimal number of items /// /// @returns OK or FAIL (out of memory). static int hash_may_resize(hashtab_T *ht, int minitems) { hashitem_T temparray[HT_INIT_SIZE]; hashitem_T *oldarray, *newarray; hashitem_T *olditem, *newitem; unsigned newi; int todo; long_u oldsize, newsize; long_u minsize; long_u newmask; hash_T perturb; // Don't resize a locked table. if (ht->ht_locked > 0) { return OK; } #ifdef HT_DEBUG if (ht->ht_used > ht->ht_filled) { EMSG("hash_may_resize(): more used than filled"); } if (ht->ht_filled >= ht->ht_mask + 1) { EMSG("hash_may_resize(): table completely filled"); } #endif // ifdef HT_DEBUG if (minitems == 0) { // Return quickly for small tables with at least two NULL items. NULL // items are required for the lookup to decide a key isn't there. if ((ht->ht_filled < HT_INIT_SIZE - 1) && (ht->ht_array == ht->ht_smallarray)) { return OK; } // Grow or refill the array when it's more than 2/3 full (including // removed items, so that they get cleaned up). // Shrink the array when it's less than 1/5 full. When growing it is // at least 1/4 full (avoids repeated grow-shrink operations) oldsize = ht->ht_mask + 1; if ((ht->ht_filled * 3 < oldsize * 2) && (ht->ht_used > oldsize / 5)) { return OK; } if (ht->ht_used > 1000) { // it's big, don't make too much room minsize = ht->ht_used * 2; } else { // make plenty of room minsize = ht->ht_used * 4; } } else { // Use specified size. if ((long_u)minitems < ht->ht_used) { // just in case... minitems = (int)ht->ht_used; } // array is up to 2/3 full minsize = minitems * 3 / 2; } newsize = HT_INIT_SIZE; while (newsize < minsize) { // make sure it's always a power of 2 newsize <<= 1; if (newsize == 0) { // overflow return FAIL; } } if (newsize == HT_INIT_SIZE) { // Use the small array inside the hashdict structure. newarray = ht->ht_smallarray; if (ht->ht_array == newarray) { // Moving from ht_smallarray to ht_smallarray! Happens when there // are many removed items. Copy the items to be able to clean up // removed items. mch_memmove(temparray, newarray, sizeof(temparray)); oldarray = temparray; } else { oldarray = ht->ht_array; } } else { // Allocate an array. newarray = (hashitem_T *)alloc((unsigned)(sizeof(hashitem_T) * newsize)); if (newarray == NULL) { // Out of memory. When there are NULL items still return OK. // Otherwise set ht_error, because lookup may result in a hang if // we add another item. if (ht->ht_filled < ht->ht_mask) { return OK; } ht->ht_error = TRUE; return FAIL; } oldarray = ht->ht_array; } memset(newarray, 0, (size_t)(sizeof(hashitem_T) * newsize)); // Move all the items from the old array to the new one, placing them in // the right spot. The new array won't have any removed items, thus this // is also a cleanup action. newmask = newsize - 1; todo = (int)ht->ht_used; for (olditem = oldarray; todo > 0; ++olditem) { if (!HASHITEM_EMPTY(olditem)) { // The algorithm to find the spot to add the item is identical to // the algorithm to find an item in hash_lookup(). But we only // need to search for a NULL key, thus it's simpler. newi = (unsigned)(olditem->hi_hash & newmask); newitem = &newarray[newi]; if (newitem->hi_key != NULL) { for (perturb = olditem->hi_hash;; perturb >>= PERTURB_SHIFT) { newi = (unsigned)((newi << 2U) + newi + perturb + 1U); newitem = &newarray[newi & newmask]; if (newitem->hi_key == NULL) { break; } } } *newitem = *olditem; todo--; } }