From df040e55fbd3edc5a36462af927a7194d079d0b8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:01:09 +0300 Subject: eval/typval: Add tv_dict_add_allocated_str() function --- src/nvim/eval/typval.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4521085519..c339a5cdd2 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1386,12 +1386,33 @@ int tv_dict_add_str(dict_T *const d, const char *const key, const size_t key_len, const char *const val) FUNC_ATTR_NONNULL_ALL +{ + return tv_dict_add_allocated_str(d, key, key_len, xstrdup(val)); +} + +/// Add a string entry to dictionary +/// +/// Unlike tv_dict_add_str() saves val to the new dictionary item in place of +/// creating a new copy. +/// +/// @warning String will be freed even in case addition fails. +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val String to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_allocated_str(dict_T *const d, + const char *const key, const size_t key_len, + char *const val) + FUNC_ATTR_NONNULL_ALL { dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_STRING; - item->di_tv.vval.v_string = (char_u *)xstrdup(val); + item->di_tv.vval.v_string = (char_u *)val; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; -- cgit From 3a923ad2db87b2bece89616b28a14ab9826d569a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 17 Jul 2017 02:33:18 +0300 Subject: ex_getln: Replace global with entry in save_ccline --- src/nvim/eval/typval.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3f8ed3b3f9..c44b85644d 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -43,7 +43,7 @@ typedef struct partial_S partial_T; typedef struct ufunc ufunc_T; typedef enum { - kCallbackNone, + kCallbackNone = 0, kCallbackFuncref, kCallbackPartial, } CallbackType; -- cgit From 8f03014e8861df7f49bf4e1dbdfcc20e1611ce35 Mon Sep 17 00:00:00 2001 From: Adrian Neumann Date: Thu, 28 Sep 2017 10:19:57 +0200 Subject: eval/decode.c: Avoid NULL arg to memchr() #7332 Clang complains because memchr has undefined behavior if the ptr is NULL, even if len==0. Helped-by: Nikolai Aleksandrovich Pavlov --- src/nvim/eval/decode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 9c9c2c2dc8..0933b1bf9c 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -266,7 +266,7 @@ typval_T decode_string(const char *const s, const size_t len, { assert(s != NULL || len == 0); const bool really_hasnul = (hasnul == kNone - ? memchr(s, NUL, len) != NULL + ? ((s != NULL) && (memchr(s, NUL, len) != NULL)) : (bool)hasnul); if (really_hasnul) { list_T *const list = tv_list_alloc(); -- cgit From a4f6cec7a31ff8dbfa089b9e22227afbeb951e9b Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 22 Nov 2017 22:35:20 +0100 Subject: cmdline: CmdlineEnter and CmdlineLeave autocommands (#7422) vim-patch:fafcf0dd59fd patch 8.0.1206: no autocmd for entering or leaving the command line Problem: No autocmd for entering or leaving the command line. Solution: Add CmdlineEnter and CmdlineLeave. https://github.com/vim/vim/commit/fafcf0dd59fd9c4ef743bb333ae40d1d322b6079 --- src/nvim/eval/typval.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index c339a5cdd2..262ea922ef 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1374,6 +1374,29 @@ int tv_dict_add_nr(dict_T *const d, const char *const key, return OK; } +/// Add a special entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val SpecialVarValue to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_special(dict_T *const d, const char *const key, + const size_t key_len, SpecialVarValue val) +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_SPECIAL; + item->di_tv.vval.v_special = val; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + /// Add a string entry to dictionary /// /// @param[out] d Dictionary to add entry to. -- cgit From 5215e3205a07b85e4e4cf1f8a8ca6be2b9556459 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 27 Aug 2017 11:59:33 +0200 Subject: channels: refactor --- src/nvim/eval/typval.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 262ea922ef..99382d2a24 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -847,6 +847,30 @@ bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) return false; } +/// Unref/free callback +void callback_free(Callback *const callback) + FUNC_ATTR_NONNULL_ALL +{ + switch (callback->type) { + case kCallbackFuncref: { + func_unref(callback->data.funcref); + xfree(callback->data.funcref); + break; + } + case kCallbackPartial: { + partial_unref(callback->data.partial); + break; + } + case kCallbackNone: { + break; + } + default: { + abort(); + } + } + callback->type = kCallbackNone; +} + /// Remove watcher from a dictionary /// /// @param dict Dictionary to remove watcher from. -- cgit From 1ebc96fe10fbdbec22caa26d5d52a9f095da9687 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 5 Jun 2017 08:29:10 +0200 Subject: channels: allow bytes sockets and stdio, and buffered bytes output --- src/nvim/eval/typval.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 99382d2a24..4bc3a85efb 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -374,7 +374,7 @@ void tv_list_append_dict(list_T *const list, dict_T *const dict) /// case string is considered to be usual zero-terminated /// string or NULL “empty” string. void tv_list_append_string(list_T *const l, const char *const str, - const ptrdiff_t len) + const ssize_t len) FUNC_ATTR_NONNULL_ARG(1) { if (str == NULL) { @@ -824,7 +824,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, /// @param[in] cb2 Second callback to check. /// /// @return True if they are equal, false otherwise. -bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) +bool tv_callback_equal(const Callback *cb1, const Callback *cb2) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (cb1->type != cb2->type) { @@ -843,12 +843,12 @@ bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) return true; } } - assert(false); + abort(); return false; } /// Unref/free callback -void callback_free(Callback *const callback) +void callback_free(Callback *callback) FUNC_ATTR_NONNULL_ALL { switch (callback->type) { @@ -864,9 +864,6 @@ void callback_free(Callback *const callback) case kCallbackNone: { break; } - default: { - abort(); - } } callback->type = kCallbackNone; } -- cgit From ddce5bca03067ce7adf70496a3493cf12c016364 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 00:08:44 +0300 Subject: eval/typval: Add functions useful for hiding list implementation --- src/nvim/eval/typval.h | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index c44b85644d..9caf3f7b60 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -284,6 +284,54 @@ typedef struct list_stack_S { #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) +static inline void tv_list_ref(list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Increase reference count for a given list +/// +/// Does nothing for NULL lists. +/// +/// @param[in] l List to modify. +static inline void tv_list_ref(list_T *const l) +{ + if (l == NULL) { + return; + } + l->lv_refcount++; +} + +static inline VarLockStatus tv_list_locked(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get list lock status +/// +/// Returns VAR_FIXED for NULL lists. +/// +/// @param[in] l List to check. +static inline VarLockStatus tv_list_locked(const list_T *const l) +{ + if (l == NULL) { + return VAR_FIXED; + } + return l->lv_lock; +} + +/// Set list lock status +/// +/// May only “set” VAR_FIXED for NULL lists. +/// +/// @param[out] l List to modify. +/// @param[in] lock New lock status. +static inline void tv_list_set_lock(list_T *const l, + const VarLockStatus lock) +{ + if (l == NULL) { + assert(lock == VAR_FIXED); + return; + } + l->lv_lock = lock; +} + static inline long tv_list_len(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; @@ -298,6 +346,22 @@ static inline long tv_list_len(const list_T *const l) return l->lv_len; } +static inline listitem_T *tv_list_first(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get first list item +/// +/// @param[in] l List to get item from. +/// +/// @return List item or NULL in case of an empty list. +static inline listitem_T *tv_list_first(const list_T *const l) +{ + if (l == NULL) { + return NULL; + } + return l->lv_first; +} + static inline long tv_dict_len(const dict_T *const d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; @@ -352,6 +416,46 @@ extern const char *const tv_empty_string; /// Specifies that free_unref_items() function has (not) been entered extern bool tv_in_free_unref_items; +/// Iterate over a list +/// +/// @param modifier Modifier: expected to be const or nothing, volatile should +/// also work if you have any uses for the volatile list. +/// @param[in] l List to iterate over. +/// @param li Name of the variable with current listitem_T entry. +/// @param code Cycle body. +#define _TV_LIST_ITER_MOD(modifier, l, li, code) \ + do { \ + modifier list_T *const l_ = (l); \ + if (l_ != NULL) { \ + for (modifier listitem_T *const li = l_->lv_first; \ + li != NULL; li = li->li_next) { \ + code \ + } \ + } \ + } while (0) + +/// Iterate over a list +/// +/// To be used when you need to modify list or values you iterate over, use +/// #TV_LIST_ITER_CONST if you don’t. +/// +/// @param[in] l List to iterate over. +/// @param li Name of the variable with current listitem_T entry. +/// @param code Cycle body. +#define TV_LIST_ITER(l, li, code) \ + _TV_LIST_ITER_MOD(, l, li, code) + +/// Iterate over a list +/// +/// To be used when you don’t need to modify list or values you iterate over, +/// use #TV_LIST_ITER if you do. +/// +/// @param[in] l List to iterate over. +/// @param li Name of the variable with current listitem_T entry. +/// @param code Cycle body. +#define TV_LIST_ITER_CONST(l, li, code) \ + _TV_LIST_ITER_MOD(const, l, li, code) + /// Iterate over a dictionary /// /// @param[in] d Dictionary to iterate over. -- cgit From 49dd615693c9f6d86a180371abcd09d32ac8aeca Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 00:20:05 +0300 Subject: eval/typval: Add macros useful for hiding list item implementation --- src/nvim/eval/typval.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 9caf3f7b60..b00f43f625 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -456,6 +456,35 @@ extern bool tv_in_free_unref_items; #define TV_LIST_ITER_CONST(l, li, code) \ _TV_LIST_ITER_MOD(const, l, li, code) +// Below macros are macros to avoid duplicating code for functionally identical +// const and non-const function variants. + +/// Get typval_T out of list item +/// +/// @param[in] li List item to get typval_T from, must not be NULL. +/// +/// @return Pointer to typval_T. +#define TV_LIST_ITEM_TV(li) (&(li)->li_tv) + +/// Get next list item given the current one +/// +/// @param[in] l List to get item from. +/// @param[in] li List item to get typval_T from. +/// +/// @return Pointer to the next item or NULL. +#define TV_LIST_ITEM_NEXT(l, li) ((li)->li_next) + +/// Get previous list item given the current one +/// +/// @param[in] l List to get item from. +/// @param[in] li List item to get typval_T from. +/// +/// @return Pointer to the previous item or NULL. +#define TV_LIST_ITEM_PREV(l, li) ((li)->li_prev) +// List argument is not used currently, but it is a must for lists implemented +// as a pair (size(in list), array) without terminator - basically for lists on +// top of kvec. + /// Iterate over a dictionary /// /// @param[in] d Dictionary to iterate over. -- cgit From 274f32d42e61e6f6c76b9ca499f5b79f256a481a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 03:39:51 +0300 Subject: *: Start hiding list implementation Most of files, except for eval.c and eval/* were only processed by perl. --- src/nvim/eval/decode.c | 16 +++--- src/nvim/eval/typval.c | 131 ++++++++++++++++++++++++------------------------- src/nvim/eval/typval.h | 33 ++++++++++--- 3 files changed, 98 insertions(+), 82 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 0933b1bf9c..e3098683a4 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -61,7 +61,7 @@ static inline void create_special_dict(typval_T *const rettv, type_di->di_tv.v_type = VAR_LIST; type_di->di_tv.v_lock = VAR_UNLOCKED; type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; - type_di->di_tv.vval.v_list->lv_refcount++; + tv_list_ref(type_di->di_tv.vval.v_list); tv_dict_add(dict, type_di); dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); val_di->di_tv = val; @@ -234,7 +234,7 @@ list_T *decode_create_map_special_dict(typval_T *const ret_tv) FUNC_ATTR_NONNULL_ALL { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); create_special_dict(ret_tv, kMPMap, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -270,7 +270,7 @@ typval_T decode_string(const char *const s, const size_t len, : (bool)hasnul); if (really_hasnul) { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); typval_T tv; create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -849,7 +849,7 @@ json_decode_string_cycle_start: } case '[': { list_T *list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); typval_T tv = { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -970,7 +970,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; } else { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -993,7 +993,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; } else { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -1039,7 +1039,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } case MSGPACK_OBJECT_ARRAY: { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); *rettv = (typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -1110,7 +1110,7 @@ msgpack_to_vim_generic_map: {} } case MSGPACK_OBJECT_EXT: { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); tv_list_append_number(list, mobj.via.ext.type); list_T *const ext_val_list = tv_list_alloc(); tv_list_append_list(list, ext_val_list); diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4bc3a85efb..eaf70b41fd 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -66,7 +66,7 @@ listitem_T *tv_list_item_alloc(void) void tv_list_item_free(listitem_T *const item) FUNC_ATTR_NONNULL_ALL { - tv_clear(&item->li_tv); + tv_clear(TV_LIST_ITEM_TV(item)); xfree(item); } @@ -326,7 +326,7 @@ void tv_list_append_tv(list_T *const l, typval_T *const tv) FUNC_ATTR_NONNULL_ALL { listitem_T *const li = tv_list_item_alloc(); - tv_copy(tv, &li->li_tv); + tv_copy(tv, TV_LIST_ITEM_TV(li)); tv_list_append(l, li); } @@ -339,12 +339,12 @@ void tv_list_append_list(list_T *const list, list_T *const itemlist) { listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_list = itemlist; + TV_LIST_ITEM_TV(li)->v_type = VAR_LIST; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_list = itemlist; tv_list_append(list, li); if (itemlist != NULL) { - itemlist->lv_refcount++; + tv_list_ref(itemlist); } } @@ -357,9 +357,9 @@ void tv_list_append_dict(list_T *const list, dict_T *const dict) { listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_DICT; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_dict = dict; + TV_LIST_ITEM_TV(li)->v_type = VAR_DICT; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_dict = dict; tv_list_append(list, li); if (dict != NULL) { dict->dv_refcount++; @@ -399,9 +399,9 @@ void tv_list_append_allocated_string(list_T *const l, char *const str) listitem_T *const li = tv_list_item_alloc(); tv_list_append(l, li); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_string = (char_u *)str; + TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)str; } /// Append number to the list @@ -412,9 +412,9 @@ void tv_list_append_allocated_string(list_T *const l, char *const str) void tv_list_append_number(list_T *const l, const varnumber_T n) { listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_NUMBER; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_number = n; + TV_LIST_ITEM_TV(li)->v_type = VAR_NUMBER; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_number = n; tv_list_append(l, li); } @@ -439,33 +439,32 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, } list_T *copy = tv_list_alloc(); + tv_list_ref(copy); if (copyID != 0) { // Do this before adding the items, because one of the items may // refer back to this list. orig->lv_copyID = copyID; orig->lv_copylist = copy; } - listitem_T *item; - for (item = orig->lv_first; item != NULL && !got_int; - item = item->li_next) { + TV_LIST_ITER(orig, item, { listitem_T *const ni = tv_list_item_alloc(); if (deep) { - if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { + if (var_item_copy(conv, TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni), + deep, copyID) == FAIL) { xfree(ni); - break; + goto tv_list_copy_error; } } else { - tv_copy(&item->li_tv, &ni->li_tv); + tv_copy(TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni)); } tv_list_append(copy, ni); - } - copy->lv_refcount++; - if (item != NULL) { - tv_list_unref(copy); - copy = NULL; - } + }); return copy; + +tv_list_copy_error: + tv_list_unref(copy); + return NULL; } /// Extend first list with the second @@ -475,17 +474,17 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, /// @param[in] bef If not NULL, extends before this item. void tv_list_extend(list_T *const l1, list_T *const l2, listitem_T *const bef) - FUNC_ATTR_NONNULL_ARG(1, 2) + FUNC_ATTR_NONNULL_ARG(1) { - int todo = l2->lv_len; + int todo = tv_list_len(l2); listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev); listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next); // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. - for (listitem_T *item = l2->lv_first - ; item != NULL && --todo >= 0 + for (listitem_T *item = tv_list_first(l2) + ; item != NULL && todo-- ; item = (item == befbef ? saved_next : item->li_next)) { - tv_list_insert_tv(l1, &item->li_tv, bef); + tv_list_insert_tv(l1, TV_LIST_ITEM_TV(item), bef); } } @@ -540,13 +539,12 @@ static int list_join_inner(garray_T *const gap, list_T *const l, { size_t sumlen = 0; bool first = true; - listitem_T *item; // Stringify each item in the list. - for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { + TV_LIST_ITER(l, item, { char *s; size_t len; - s = encode_tv2echo(&item->li_tv, &len); + s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len); if (s == NULL) { return FAIL; } @@ -557,7 +555,7 @@ static int list_join_inner(garray_T *const gap, list_T *const l, p->tofree = p->s = (char_u *)s; line_breakcheck(); - } + }); // Allocate result buffer with its total size, avoid re-allocation and // multiple copy operations. Add 2 for a tailing ']' and NUL. @@ -591,16 +589,16 @@ static int list_join_inner(garray_T *const gap, list_T *const l, /// /// @return OK in case of success, FAIL otherwise. int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(1) { - if (l->lv_len < 1) { + if (!tv_list_len(l)) { return OK; } garray_T join_ga; int retval; - ga_init(&join_ga, (int)sizeof(Join), l->lv_len); + ga_init(&join_ga, (int)sizeof(Join), tv_list_len(l)); retval = list_join_inner(gap, l, sep, &join_ga); #define FREE_JOIN_TOFREE(join) xfree((join)->tofree) @@ -632,11 +630,13 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, return false; } - listitem_T *item1 = l1->lv_first; - listitem_T *item2 = l2->lv_first; + listitem_T *item1 = tv_list_first(l1); + listitem_T *item2 = tv_list_first(l2); for (; item1 != NULL && item2 != NULL - ; item1 = item1->li_next, item2 = item2->li_next) { - if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) { + ; item1 = TV_LIST_ITEM_NEXT(l1, item1), + item2 = TV_LIST_ITEM_NEXT(n2, item2)) { + if (!tv_equal(TV_LIST_ITEM_TV(item1), TV_LIST_ITEM_TV(item2), ic, + recursive)) { return false; } } @@ -662,13 +662,8 @@ listitem_T *tv_list_find(list_T *const l, int n) return NULL; } - // Negative index is relative to the end. - if (n < 0) { - n = l->lv_len + n; - } - - // Check for index out of range. - if (n < 0 || n >= l->lv_len) { + n = tv_list_uidx(l, n); + if (n == -1) { return NULL; } @@ -740,7 +735,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error) } return -1; } - return tv_get_number_chk(&li->li_tv, ret_error); + return tv_get_number_chk(TV_LIST_ITEM_TV(li), ret_error); } /// Get list item l[n] as a string @@ -757,7 +752,7 @@ const char *tv_list_find_str(list_T *const l, const int n) emsgf(_(e_listidx), (int64_t)n); return NULL; } - return tv_get_string(&li->li_tv); + return tv_get_string(TV_LIST_ITEM_TV(li)); } /// Locate item in a list and return its index @@ -772,15 +767,14 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) if (l == NULL) { return -1; } - long idx = 0; - const listitem_T *li; - for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { + int idx = 0; + TV_LIST_ITER_CONST(l, li, { + if (li == item) { + return idx; + } idx++; - } - if (li == NULL) { - return -1; - } - return idx; + }); + return -1; } //{{{1 Dictionaries @@ -1339,7 +1333,7 @@ int tv_dict_add_list(dict_T *const d, const char *const key, item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_LIST; item->di_tv.vval.v_list = list; - list->lv_refcount++; + tv_list_ref(list); if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; @@ -1677,7 +1671,7 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv) ret_tv->vval.v_list = l; ret_tv->v_type = VAR_LIST; ret_tv->v_lock = VAR_UNLOCKED; - l->lv_refcount++; + tv_list_ref(l); return l; } @@ -2027,7 +2021,7 @@ void tv_copy(typval_T *const from, typval_T *const to) } case VAR_LIST: { if (from->vval.v_list != NULL) { - to->vval.v_list->lv_refcount++; + tv_list_ref(to->vval.v_list); } break; } @@ -2084,9 +2078,9 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) CHANGE_LOCK(lock, l->lv_lock); if (deep < 0 || deep > 1) { // Recursive: lock/unlock the items the List contains. - for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { - tv_item_lock(&li->li_tv, deep - 1, lock); - } + TV_LIST_ITER(l, li, { + tv_item_lock(TV_LIST_ITEM_TV(li), deep - 1, lock); + }); } } break; @@ -2122,6 +2116,8 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) /// Check whether VimL value is locked itself or refers to a locked container /// +/// @warning Fixed container is not the same as locked. +/// /// @param[in] tv Value to check. /// /// @return True if value is locked, false otherwise. @@ -2130,8 +2126,7 @@ bool tv_islocked(const typval_T *const tv) { return ((tv->v_lock == VAR_LOCKED) || (tv->v_type == VAR_LIST - && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock == VAR_LOCKED)) + && (tv_list_locked(tv->vval.v_list) == VAR_LOCKED)) || (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL && (tv->vval.v_dict->dv_lock == VAR_LOCKED))); diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index b00f43f625..2a0c52b4be 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "nvim/types.h" #include "nvim/hashtab.h" @@ -284,9 +285,6 @@ typedef struct list_stack_S { #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) -static inline void tv_list_ref(list_T *const l) - REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; - /// Increase reference count for a given list /// /// Does nothing for NULL lists. @@ -332,13 +330,13 @@ static inline void tv_list_set_lock(list_T *const l, l->lv_lock = lock; } -static inline long tv_list_len(const list_T *const l) +static inline int tv_list_len(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. -static inline long tv_list_len(const list_T *const l) +static inline int tv_list_len(const list_T *const l) { if (l == NULL) { return 0; @@ -346,6 +344,29 @@ static inline long tv_list_len(const list_T *const l) return l->lv_len; } +static inline int tv_list_uidx(const list_T *const l, int n) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Normalize index: that is, return either -1 or non-negative index +/// +/// @param[in] l List to intex. Used to get length. +/// @param[in] n List index, possibly negative. +/// +/// @return -1 or list index in range [0, tv_list_len(l)). +static inline int tv_list_uidx(const list_T *const l, int n) +{ + // Negative index is relative to the end. + if (n < 0) { + n += tv_list_len(l); + } + + // Check for index out of range. + if (n < 0 || n >= tv_list_len(l)) { + return -1; + } + return n; +} + static inline listitem_T *tv_list_first(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; @@ -427,7 +448,7 @@ extern bool tv_in_free_unref_items; do { \ modifier list_T *const l_ = (l); \ if (l_ != NULL) { \ - for (modifier listitem_T *const li = l_->lv_first; \ + for (modifier listitem_T *li = l_->lv_first; \ li != NULL; li = li->li_next) { \ code \ } \ -- cgit From ac4bbf55f6d6b9b252dd90fe800626850022b690 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 22:04:43 +0300 Subject: *: Hide list implementation in other files as well --- src/nvim/eval/decode.c | 28 +++++---- src/nvim/eval/encode.c | 76 ++++++++++++------------ src/nvim/eval/encode.h | 13 +++-- src/nvim/eval/typval.h | 16 +++++ src/nvim/eval/typval_encode.c.h | 125 ++++++++++++++++++++++++---------------- 5 files changed, 152 insertions(+), 106 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index e3098683a4..b2d901462a 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -120,7 +120,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, last_container = kv_last(*container_stack); } if (last_container.container.v_type == VAR_LIST) { - if (last_container.container.vval.v_list->lv_len != 0 + if (tv_list_len(last_container.container.vval.v_list) != 0 && !obj.didcomma) { EMSG2(_("E474: Expected comma before list item: %s"), val_location); tv_clear(&obj.val); @@ -128,7 +128,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, } assert(last_container.special_val == NULL); listitem_T *obj_li = tv_list_item_alloc(); - obj_li->li_tv = obj.val; + *TV_LIST_ITEM_TV(obj_li) = obj.val; tv_list_append(last_container.container.vval.v_list, obj_li); } else if (last_container.stack_index == kv_size(*stack) - 2) { if (!obj.didcolon) { @@ -155,10 +155,10 @@ static inline int json_decoder_pop(ValuesStackItem obj, list_T *const kv_pair = tv_list_alloc(); tv_list_append_list(last_container.special_val, kv_pair); listitem_T *const key_li = tv_list_item_alloc(); - key_li->li_tv = key.val; + *TV_LIST_ITEM_TV(key_li) = key.val; tv_list_append(kv_pair, key_li); listitem_T *const val_li = tv_list_item_alloc(); - val_li->li_tv = obj.val; + *TV_LIST_ITEM_TV(val_li) = obj.val; tv_list_append(kv_pair, val_li); } } else { @@ -738,8 +738,9 @@ json_decode_string_cycle_start: } else if (last_container.special_val == NULL ? (last_container.container.v_type == VAR_DICT ? (DICT_LEN(last_container.container.vval.v_dict) == 0) - : (last_container.container.vval.v_list->lv_len == 0)) - : (last_container.special_val->lv_len == 0)) { + : (tv_list_len(last_container.container.vval.v_list) + == 0)) + : (tv_list_len(last_container.special_val) == 0)) { emsgf(_("E474: Leading comma: %.*s"), LENP(p, e)); goto json_decode_string_fail; } @@ -1047,9 +1048,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; for (size_t i = 0; i < mobj.via.array.size; i++) { listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_UNKNOWN; + TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN; tv_list_append(list, li); - if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { + if (msgpack_to_vim(mobj.via.array.ptr[i], TV_LIST_ITEM_TV(li)) + == FAIL) { return FAIL; } } @@ -1094,15 +1096,17 @@ msgpack_to_vim_generic_map: {} list_T *const kv_pair = tv_list_alloc(); tv_list_append_list(list, kv_pair); listitem_T *const key_li = tv_list_item_alloc(); - key_li->li_tv.v_type = VAR_UNKNOWN; + TV_LIST_ITEM_TV(key_li)->v_type = VAR_UNKNOWN; tv_list_append(kv_pair, key_li); listitem_T *const val_li = tv_list_item_alloc(); - val_li->li_tv.v_type = VAR_UNKNOWN; + TV_LIST_ITEM_TV(val_li)->v_type = VAR_UNKNOWN; tv_list_append(kv_pair, val_li); - if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { + if (msgpack_to_vim(mobj.via.map.ptr[i].key, TV_LIST_ITEM_TV(key_li)) + == FAIL) { return FAIL; } - if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) { + if (msgpack_to_vim(mobj.via.map.ptr[i].val, TV_LIST_ITEM_TV(val_li)) + == FAIL) { return FAIL; } } diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index ef647b3ee4..0e5db84ad5 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -53,17 +53,18 @@ int encode_list_write(void *const data, const char *const buf, const size_t len) list_T *const list = (list_T *) data; const char *const end = buf + len; const char *line_end = buf; - listitem_T *li = list->lv_last; + listitem_T *li = tv_list_last(list); // Continue the last list element if (li != NULL) { line_end = xmemscan(buf, NL, len); if (line_end != buf) { const size_t line_length = (size_t)(line_end - buf); - char *str = (char *)li->li_tv.vval.v_string; + char *str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string; const size_t li_len = (str == NULL ? 0 : strlen(str)); - li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1); - str = (char *)li->li_tv.vval.v_string + li_len; + TV_LIST_ITEM_TV(li)->vval.v_string = xrealloc( + str, li_len + line_length + 1); + str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string + li_len; memcpy(str, buf, line_length); str[line_length] = 0; memchrsub(str, NUL, NL, line_length); @@ -135,21 +136,18 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, } case kMPConvPairs: case kMPConvList: { - int idx = 0; - const listitem_T *li; - for (li = v.data.l.list->lv_first; - li != NULL && li->li_next != v.data.l.li; - li = li->li_next) { - idx++; - } + const listitem_T *const li = TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li); + int idx = (int)tv_list_idx_of_item(v.data.l.list, li); if (v.type == kMPConvList || li == NULL - || (li->li_tv.v_type != VAR_LIST - && li->li_tv.vval.v_list->lv_len <= 0)) { - vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx); + || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST + && tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) { + vim_snprintf((char *)IObuff, IOSIZE, idx_msg, idx); ga_concat(&msg_ga, IObuff); } else { - typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv; + typval_T key_tv = *TV_LIST_ITEM_TV( + tv_list_first(TV_LIST_ITEM_TV(li)->vval.v_list)); char *const key = encode_tv2echo(&key_tv, NULL); vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx); xfree(key); @@ -202,21 +200,17 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT { size_t len = 0; - if (list != NULL) { - for (const listitem_T *li = list->lv_first; - li != NULL; - li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { - return false; - } - len++; - if (li->li_tv.vval.v_string != 0) { - len += STRLEN(li->li_tv.vval.v_string); - } + TV_LIST_ITER_CONST(list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { + return false; } - if (len) { - len--; + len++; + if (TV_LIST_ITEM_TV(li)->vval.v_string != 0) { + len += STRLEN(TV_LIST_ITEM_TV(li)->vval.v_string); } + }); + if (len) { + len--; } *ret_len = len; if (len == 0) { @@ -253,31 +247,34 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, char *const buf_end = buf + nbuf; char *p = buf; while (p < buf_end) { - assert(state->li_length == 0 || state->li->li_tv.vval.v_string != NULL); + assert(state->li_length == 0 + || TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL); for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) { - assert(state->li->li_tv.vval.v_string != NULL); - const char ch = (char)state->li->li_tv.vval.v_string[state->offset++]; + assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL); + const char ch = (char)( + TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++]); *p++ = (char)((char)ch == (char)NL ? (char)NUL : (char)ch); } if (p < buf_end) { - state->li = state->li->li_next; + state->li = TV_LIST_ITEM_NEXT(state->list, state->li); if (state->li == NULL) { *read_bytes = (size_t) (p - buf); return OK; } *p++ = NL; - if (state->li->li_tv.v_type != VAR_STRING) { + if (TV_LIST_ITEM_TV(state->li)->v_type != VAR_STRING) { *read_bytes = (size_t) (p - buf); return FAIL; } state->offset = 0; - state->li_length = (state->li->li_tv.vval.v_string == NULL + state->li_length = (TV_LIST_ITEM_TV(state->li)->vval.v_string == NULL ? 0 - : STRLEN(state->li->li_tv.vval.v_string)); + : STRLEN(TV_LIST_ITEM_TV(state->li)->vval.v_string)); } } *read_bytes = nbuf; - return (state->offset < state->li_length || state->li->li_next != NULL + return ((state->offset < state->li_length + || TV_LIST_ITEM_NEXT(state->list, state->li) != NULL) ? NOTDONE : OK); } @@ -727,12 +724,11 @@ bool encode_check_json_key(const typval_T *const tv) if (val_di->di_tv.vval.v_list == NULL) { return true; } - for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first; - li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { + TV_LIST_ITER_CONST(val_di->di_tv.vval.v_list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { return false; } - } + }); return true; } diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index 9bc665253b..ccea245ab3 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -33,9 +33,10 @@ int encode_vim_to_echo(garray_T *const packer, /// Structure defining state for read_from_list() typedef struct { + const list_T *const list; ///< List being currently read. const listitem_T *li; ///< Item currently read. - size_t offset; ///< Byte offset inside the read item. - size_t li_length; ///< Length of the string inside the read item. + size_t offset; ///< Byte offset inside the read item. + size_t li_length; ///< Length of the string inside the read item. } ListReaderState; /// Initialize ListReaderState structure @@ -43,11 +44,13 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list) FUNC_ATTR_NONNULL_ALL { return (ListReaderState) { - .li = list->lv_first, + .list = list, + .li = tv_list_first(list), .offset = 0, - .li_length = (list->lv_first->li_tv.vval.v_string == NULL + .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL ? 0 - : STRLEN(list->lv_first->li_tv.vval.v_string)), + : STRLEN(TV_LIST_ITEM_TV( + tv_list_first(list))->vval.v_string)), }; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 2a0c52b4be..a05abd8a53 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -383,6 +383,22 @@ static inline listitem_T *tv_list_first(const list_T *const l) return l->lv_first; } +static inline listitem_T *tv_list_last(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get last list item +/// +/// @param[in] l List to get item from. +/// +/// @return List item or NULL in case of an empty list. +static inline listitem_T *tv_list_last(const list_T *const l) +{ + if (l == NULL) { + return NULL; + } + return l->lv_last; +} + static inline long tv_dict_len(const dict_T *const d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index a93ad2dbba..800760cf7b 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -355,14 +355,14 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( break; } case VAR_LIST: { - if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { + if (tv->vval.v_list == NULL || tv_list_len(tv->vval.v_list) == 0) { TYPVAL_ENCODE_CONV_EMPTY_LIST(tv); break; } const int saved_copyID = tv->vval.v_list->lv_copyID; _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID, kMPConvList); - TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len); + TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list)); assert(saved_copyID != copyID && saved_copyID != copyID - 1); _mp_push(*mpstack, ((MPConvStackVal) { .type = kMPConvList, @@ -371,7 +371,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .data = { .l = { .list = tv->vval.v_list, - .li = tv->vval.v_list->lv_first, + .li = tv_list_first(tv->vval.v_list), }, }, })); @@ -440,23 +440,43 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( // bits is not checked), other unsigned and have at most 31 // non-zero bits (number of bits is not checked). if (val_di->di_tv.v_type != VAR_LIST - || (val_list = val_di->di_tv.vval.v_list) == NULL - || val_list->lv_len != 4 - || val_list->lv_first->li_tv.v_type != VAR_NUMBER - || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 - || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER - || (highest_bits = - val_list->lv_first->li_next->li_tv.vval.v_number) < 0 - || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER - || (high_bits = - val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 - || val_list->lv_last->li_tv.v_type != VAR_NUMBER - || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { + || tv_list_len(val_list = val_di->di_tv.vval.v_list) != 4) { goto _convert_one_value_regular_dict; } - uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62) - | (uint64_t)(((uint64_t)high_bits) << 31) - | (uint64_t)low_bits); + + const listitem_T *const sign_li = tv_list_first(val_list); + if (TV_LIST_ITEM_TV(sign_li)->v_type != VAR_NUMBER + || (sign = TV_LIST_ITEM_TV(sign_li)->vval.v_number) == 0) { + goto _convert_one_value_regular_dict; + } + + const listitem_T *const highest_bits_li = ( + TV_LIST_ITEM_NEXT(val_list, sign_li)); + if (TV_LIST_ITEM_TV(highest_bits_li)->v_type != VAR_NUMBER + || ((highest_bits + = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number) + < 0)) { + goto _convert_one_value_regular_dict; + } + + const listitem_T *const high_bits_li = ( + TV_LIST_ITEM_NEXT(val_list, highest_bits_li)); + if (TV_LIST_ITEM_TV(high_bits_li)->v_type != VAR_NUMBER + || ((high_bits = TV_LIST_ITEM_TV(high_bits_li)->vval.v_number) + < 0)) { + goto _convert_one_value_regular_dict; + } + + const listitem_T *const low_bits_li = tv_list_last(val_list); + if (TV_LIST_ITEM_TV(low_bits_li)->v_type != VAR_NUMBER + || ((low_bits = TV_LIST_ITEM_TV(low_bits_li)->vval.v_number) + < 0)) { + goto _convert_one_value_regular_dict; + } + + const uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62) + | (uint64_t)(((uint64_t)high_bits) << 31) + | (uint64_t)low_bits); if (sign > 0) { TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number); } else { @@ -499,8 +519,8 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, copyID, kMPConvList); - TYPVAL_ENCODE_CONV_LIST_START(tv, - val_di->di_tv.vval.v_list->lv_len); + TYPVAL_ENCODE_CONV_LIST_START( + tv, tv_list_len(val_di->di_tv.vval.v_list)); assert(saved_copyID != copyID && saved_copyID != copyID - 1); _mp_push(*mpstack, ((MPConvStackVal) { .tv = tv, @@ -509,7 +529,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .data = { .l = { .list = val_di->di_tv.vval.v_list, - .li = val_di->di_tv.vval.v_list->lv_first, + .li = tv_list_first(val_di->di_tv.vval.v_list), }, }, })); @@ -520,22 +540,21 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( goto _convert_one_value_regular_dict; } list_T *const val_list = val_di->di_tv.vval.v_list; - if (val_list == NULL || val_list->lv_len == 0) { + if (val_list == NULL || tv_list_len(val_list) == 0) { TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR); break; } - for (const listitem_T *li = val_list->lv_first; li != NULL; - li = li->li_next) { - if (li->li_tv.v_type != VAR_LIST - || li->li_tv.vval.v_list->lv_len != 2) { + TV_LIST_ITER_CONST(val_list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST + || tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) != 2) { goto _convert_one_value_regular_dict; } - } + }); const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID; _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID, kMPConvPairs); TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR, - val_list->lv_len); + tv_list_len(val_list)); assert(saved_copyID != copyID && saved_copyID != copyID - 1); _mp_push(*mpstack, ((MPConvStackVal) { .tv = tv, @@ -544,7 +563,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .data = { .l = { .list = val_list, - .li = val_list->lv_first, + .li = tv_list_first(val_list), }, }, })); @@ -554,18 +573,22 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( const list_T *val_list; varnumber_T type; if (val_di->di_tv.v_type != VAR_LIST - || (val_list = val_di->di_tv.vval.v_list) == NULL - || val_list->lv_len != 2 - || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) - || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX + || tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2 + || (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type + != VAR_NUMBER) + || ((type + = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number) + > INT8_MAX) || type < INT8_MIN - || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { + || (TV_LIST_ITEM_TV(tv_list_last(val_list))->v_type + != VAR_LIST)) { goto _convert_one_value_regular_dict; } size_t len; char *buf; - if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, - &len, &buf)) { + if (!encode_vim_list_to_buf( + TV_LIST_ITEM_TV(tv_list_last(val_list))->vval.v_list, &len, + &buf)) { goto _convert_one_value_regular_dict; } TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type); @@ -674,11 +697,13 @@ typval_encode_stop_converting_one_item: cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID; TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv); continue; - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { + } else if (cur_mpsv->data.l.li + != tv_list_first(cur_mpsv->data.l.list)) { TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv); } - tv = &cur_mpsv->data.l.li->li_tv; - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; + tv = TV_LIST_ITEM_TV(cur_mpsv->data.l.li); + cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list, + cur_mpsv->data.l.li); break; } case kMPConvPairs: { @@ -687,24 +712,26 @@ typval_encode_stop_converting_one_item: cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID; TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); continue; - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { + } else if (cur_mpsv->data.l.li + != tv_list_first(cur_mpsv->data.l.list)) { TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS( cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); } - const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; + const list_T *const kv_pair = ( + TV_LIST_ITEM_TV(cur_mpsv->data.l.li)->vval.v_list); TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK( - encode_vim_to__error_ret, kv_pair->lv_first->li_tv); - if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, - &mpstack, cur_mpsv, - &kv_pair->lv_first->li_tv, - copyID, - objname) == FAIL) { + encode_vim_to__error_ret, *TV_LIST_ITEM_TV(tv_list_first(kv_pair))); + if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE( + TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv, + TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname) + == FAIL) { goto encode_vim_to__error_ret; } TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); - tv = &kv_pair->lv_last->li_tv; - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; + tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)); + cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list, + cur_mpsv->data.l.li); break; } case kMPConvPartial: { -- cgit From 7572d5ac5a09c18651843c026ebc92bffafe348f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Dec 2017 22:41:00 +0300 Subject: eval/encode: Fix crash in json_encode test suite --- src/nvim/eval/encode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 0e5db84ad5..50ddb1f38c 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -136,8 +136,10 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, } case kMPConvPairs: case kMPConvList: { - const listitem_T *const li = TV_LIST_ITEM_PREV(v.data.l.list, - v.data.l.li); + const listitem_T *const li = (v.data.l.li == NULL + ? tv_list_last(v.data.l.list) + : TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)); int idx = (int)tv_list_idx_of_item(v.data.l.list, li); if (v.type == kMPConvList || li == NULL -- cgit From ceb45a08858837319c8ea67b1aaeceaeb24c8510 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 01:43:36 +0300 Subject: *: Fix test failures --- src/nvim/eval/encode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 50ddb1f38c..85fd4d1578 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -140,7 +140,9 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, ? tv_list_last(v.data.l.list) : TV_LIST_ITEM_PREV(v.data.l.list, v.data.l.li)); - int idx = (int)tv_list_idx_of_item(v.data.l.list, li); + int idx = (li == NULL + ? 0 + : (int)tv_list_idx_of_item(v.data.l.list, li)); if (v.type == kMPConvList || li == NULL || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST -- cgit From d46e37cb4c71f39312233799d7f28eb86dceef60 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 10:12:59 +0300 Subject: *: Finish hiding list implementation --- src/nvim/eval/typval.c | 90 ++++++++++++++++++++++++++++++++++++++++- src/nvim/eval/typval.h | 75 +++++++++++++++++++++++++++++++++- src/nvim/eval/typval_encode.c.h | 10 ++--- 3 files changed, 168 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index eaf70b41fd..df81c0450d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -153,6 +153,45 @@ list_T *tv_list_alloc(void) return list; } +/// Initialize a static list with 10 items +/// +/// @param[out] sl Static list to initialize. +void tv_list_init_static10(staticList10_T *const sl) + FUNC_ATTR_NONNULL_ALL +{ +#define SL_SIZE ARRAY_SIZE(sl->sl_items) + list_T *const l = &sl->sl_list; + + memset(sl, 0, sizeof(staticList10_T)); + l->lv_first = &sl->sl_items[0]; + l->lv_last = &sl->sl_items[SL_SIZE - 1]; + l->lv_refcount = DO_NOT_FREE_CNT; + tv_list_set_lock(l, VAR_FIXED); + sl->sl_list.lv_len = 10; + + sl->sl_items[0].li_prev = NULL; + sl->sl_items[0].li_next = &sl->sl_items[1]; + sl->sl_items[SL_SIZE - 1].li_prev = &sl->sl_items[SL_SIZE - 2]; + sl->sl_items[SL_SIZE - 1].li_next = NULL; + + for (size_t i = 1; i < SL_SIZE - 1; i++) { + listitem_T *const li = &sl->sl_items[i]; + li->li_prev = li - 1; + li->li_next = li + 1; + } +#undef SL_SIZE +} + +/// Initialize static list with undefined number of elements +/// +/// @param[out] l List to initialize. +void tv_list_init_static(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + memset(l, 0, sizeof(*l)); + l->lv_refcount = DO_NOT_FREE_CNT; +} + /// Free items contained in a list /// /// @param[in,out] l List to clear. @@ -221,7 +260,7 @@ void tv_list_unref(list_T *const l) //{{{2 Add/remove -/// Remove items "item" to "item2" from list "l". +/// Remove items "item" to "item2" from list "l" /// /// @warning Does not free the listitem or the value! /// @@ -251,6 +290,30 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item, l->lv_idx_item = NULL; } +/// Move items "item" to "item2" from list "l" to the end of the list "tgt_l" +/// +/// @param[out] l List to move from. +/// @param[in] item First item to move. +/// @param[in] item2 Last item to move. +/// @param[out] tgt_l List to move to. +/// @param[in] cnt Number of items moved. +void tv_list_move_items(list_T *const l, listitem_T *const item, + listitem_T *const item2, list_T *const tgt_l, + const int cnt) + FUNC_ATTR_NONNULL_ALL +{ + tv_list_remove_items(l, item, item2); + item->li_prev = tgt_l->lv_last; + item2->li_next = NULL; + if (tgt_l->lv_last == NULL) { + tgt_l->lv_first = item; + } else { + tgt_l->lv_last->li_next = item; + } + tgt_l->lv_last = item2; + tgt_l->lv_len += cnt; +} + /// Insert list item /// /// @param[out] l List to insert to. @@ -644,6 +707,31 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, return true; } +/// Reverse list in-place +/// +/// @param[in,out] l List to reverse. +void tv_list_reverse(list_T *const l) +{ + if (tv_list_len(l) <= 1) { + return; + } +#define SWAP(a, b) \ + do { \ + tmp = a; \ + a = b; \ + b = tmp; \ + } while (0) + listitem_T *tmp; + + SWAP(l->lv_first, l->lv_last); + for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { + SWAP(li->li_next, li->li_prev); + } +#undef SWAP + + l->lv_idx = l->lv_len - l->lv_idx - 1; +} + //{{{2 Indexing/searching /// Locate item with a given index in a list and return it diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index a05abd8a53..a7255fdba0 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "nvim/types.h" #include "nvim/hashtab.h" @@ -27,6 +28,9 @@ typedef uint64_t uvarnumber_T; /// Type used for VimL VAR_FLOAT values typedef double float_T; +/// Refcount for dict or list that should not be freed +enum { DO_NOT_FREE_CNT = (INT_MAX / 2) }; + /// Maximal possible value of varnumber_T variable #define VARNUMBER_MAX INT64_MAX #define UVARNUMBER_MAX UINT64_MAX @@ -151,12 +155,26 @@ struct listvar_S { list_T *lv_used_prev; ///< Previous list in used lists list. }; -// Static list with 10 items. Use init_static_list() to initialize. +// Static list with 10 items. Use tv_list_init_static10() to initialize. typedef struct { list_T sl_list; // must be first listitem_T sl_items[10]; } staticList10_T; +#define TV_LIST_STATIC10_INIT { \ + .sl_list = { \ + .lv_first = NULL, \ + .lv_last = NULL, \ + .lv_refcount = 0, \ + .lv_len = 0, \ + .lv_watch = NULL, \ + .lv_idx_item = NULL, \ + .lv_lock = VAR_FIXED, \ + .lv_used_next = NULL, \ + .lv_used_prev = NULL, \ + }, \ + } + // Structure to hold an item of a Dictionary. // Also used for a variable. // The key is copied into "di_key" to avoid an extra alloc/free for it. @@ -330,6 +348,19 @@ static inline void tv_list_set_lock(list_T *const l, l->lv_lock = lock; } +/// Set list copyID +/// +/// Does not expect NULL list, be careful. +/// +/// @param[out] l List to modify. +/// @param[in] copyid New copyID. +static inline void tv_list_set_copyid(list_T *const l, + const int copyid) + FUNC_ATTR_NONNULL_ALL +{ + l->lv_copyID = copyid; +} + static inline int tv_list_len(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; @@ -344,6 +375,48 @@ static inline int tv_list_len(const list_T *const l) return l->lv_len; } +static inline int tv_list_copyid(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Get list copyID +/// +/// Does not expect NULL list, be careful. +/// +/// @param[in] l List to check. +static inline int tv_list_copyid(const list_T *const l) +{ + return l->lv_copyID; +} + +static inline list_T *tv_list_latest_copy(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Get latest list copy +/// +/// Gets lv_copylist field assigned by tv_list_copy() earlier. +/// +/// Does not expect NULL list, be careful. +/// +/// @param[in] l List to check. +static inline list_T *tv_list_latest_copy(const list_T *const l) +{ + return l->lv_copylist; +} + +/// Clear the list without freeing anything at all +/// +/// For use in sort() which saves items to a separate array and readds them back +/// after sorting via a number of tv_list_append() calls. +/// +/// @param[out] l List to clear. +static inline void tv_list_clear(list_T *const l) +{ + l->lv_first = NULL; + l->lv_last = NULL; + l->lv_idx_item = NULL; + l->lv_len = 0; +} + static inline int tv_list_uidx(const list_T *const l, int n) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 800760cf7b..e4ed2bc636 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -359,7 +359,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( TYPVAL_ENCODE_CONV_EMPTY_LIST(tv); break; } - const int saved_copyID = tv->vval.v_list->lv_copyID; + const int saved_copyID = tv_list_copyid(tv->vval.v_list); _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID, kMPConvList); TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list)); @@ -515,7 +515,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( if (val_di->di_tv.v_type != VAR_LIST) { goto _convert_one_value_regular_dict; } - const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID; + const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list); _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, copyID, kMPConvList); @@ -550,7 +550,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( goto _convert_one_value_regular_dict; } }); - const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID; + const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list); _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID, kMPConvPairs); TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR, @@ -694,7 +694,7 @@ typval_encode_stop_converting_one_item: case kMPConvList: { if (cur_mpsv->data.l.li == NULL) { (void)_mp_pop(mpstack); - cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID; + tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID); TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv); continue; } else if (cur_mpsv->data.l.li @@ -709,7 +709,7 @@ typval_encode_stop_converting_one_item: case kMPConvPairs: { if (cur_mpsv->data.l.li == NULL) { (void)_mp_pop(mpstack); - cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID; + tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID); TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); continue; } else if (cur_mpsv->data.l.li -- cgit From f4132fb38b1355115d824b7c04eff25733d059d6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 10:19:20 +0300 Subject: *: Fix linter errors --- src/nvim/eval/decode.c | 2 +- src/nvim/eval/encode.c | 2 +- src/nvim/eval/typval.c | 4 ++-- src/nvim/eval/typval_encode.c.h | 10 ++++++---- 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index b2d901462a..d5c65ebe81 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -60,7 +60,7 @@ static inline void create_special_dict(typval_T *const rettv, dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE")); type_di->di_tv.v_type = VAR_LIST; type_di->di_tv.v_lock = VAR_UNLOCKED; - type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; + type_di->di_tv.vval.v_list = (list_T *)eval_msgpack_type_lists[type]; tv_list_ref(type_di->di_tv.vval.v_list); tv_dict_add(dict, type_di); dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 85fd4d1578..9f16b78976 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -267,7 +267,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, } *p++ = NL; if (TV_LIST_ITEM_TV(state->li)->v_type != VAR_STRING) { - *read_bytes = (size_t) (p - buf); + *read_bytes = (size_t)(p - buf); return FAIL; } state->offset = 0; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index df81c0450d..0c9c4a0347 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -696,8 +696,8 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, listitem_T *item1 = tv_list_first(l1); listitem_T *item2 = tv_list_first(l2); for (; item1 != NULL && item2 != NULL - ; item1 = TV_LIST_ITEM_NEXT(l1, item1), - item2 = TV_LIST_ITEM_NEXT(n2, item2)) { + ; (item1 = TV_LIST_ITEM_NEXT(l1, item1), + item2 = TV_LIST_ITEM_NEXT(n2, item2))) { if (!tv_equal(TV_LIST_ITEM_TV(item1), TV_LIST_ITEM_TV(item2), ic, recursive)) { return false; diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index e4ed2bc636..e9531178ef 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -586,9 +586,10 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( } size_t len; char *buf; - if (!encode_vim_list_to_buf( - TV_LIST_ITEM_TV(tv_list_last(val_list))->vval.v_list, &len, - &buf)) { + if (!( + encode_vim_list_to_buf( + TV_LIST_ITEM_TV(tv_list_last(val_list))->vval.v_list, &len, + &buf))) { goto _convert_one_value_regular_dict; } TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type); @@ -721,7 +722,8 @@ typval_encode_stop_converting_one_item: TV_LIST_ITEM_TV(cur_mpsv->data.l.li)->vval.v_list); TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK( encode_vim_to__error_ret, *TV_LIST_ITEM_TV(tv_list_first(kv_pair))); - if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE( + if ( + _TYPVAL_ENCODE_CONVERT_ONE_VALUE( TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv, TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname) == FAIL) { -- cgit From 9f534422e6c3649ee40904ae3606455d26b48188 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Dec 2017 11:09:09 +0300 Subject: eval/typval: Fix typo [ci skip] --- src/nvim/eval/typval.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index a7255fdba0..c0d1790c63 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -422,7 +422,7 @@ static inline int tv_list_uidx(const list_T *const l, int n) /// Normalize index: that is, return either -1 or non-negative index /// -/// @param[in] l List to intex. Used to get length. +/// @param[in] l List to index. Used to get length. /// @param[in] n List index, possibly negative. /// /// @return -1 or list index in range [0, tv_list_len(l)). -- cgit From 932ea7a0d1d19288fad719afd52e9cbeb924c4c2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Dec 2017 01:13:04 +0300 Subject: clint,eval: Make linter check for direct usage of list attributes --- src/nvim/eval/typval.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index c0d1790c63..2bce7bd6b2 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -440,6 +440,21 @@ static inline int tv_list_uidx(const list_T *const l, int n) return n; } +static inline bool tv_list_has_watchers(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Check whether list has watchers +/// +/// E.g. is referenced by a :for loop. +/// +/// @param[in] l List to check. +/// +/// @return true if there are watchers, false otherwise. +static inline bool tv_list_has_watchers(const list_T *const l) +{ + return l && l->lv_watch; +} + static inline listitem_T *tv_list_first(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; -- cgit From 7164f618504bb1b3374da1bcc26c6cf2ac8b3f86 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 13 Dec 2017 22:22:02 +0100 Subject: typval_encode.c.h: avoid -Wnonnull-compare warning (#7712) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * typval_encode.c.h: avoid -Wnonnull-compare warning closes #6847 The NULL check is needed because TYPVAL_ENCODE_CONV_EMPTY_DICT may be invoked with literal `NULL`. Warning occurs even for `Debug` build-type: neovim/src/nvim/eval/typval.c: In function ‘_typval_encode_nothing_convert_one_value’: neovim/src/nvim/eval/typval.c:1802:10: warning: nonnull argument ‘tv’ compared to NULL [-Wnonnull-compare] if (tv != NULL) { \ ^ ../src/nvim/eval/typval_encode.c.h:398:9: note: in expansion of macro ‘TYPVAL_ENCODE_CONV_EMPTY_DICT’ TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, tv->vval.v_dict); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gcc version: gcc (Ubuntu 6.3.0-12ubuntu2) 6.3.0 20170406 * fixup! typval_encode.c.h: avoid -Wnonnull-compare warning --- src/nvim/eval/typval.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4bc3a85efb..b5382d1ccb 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1794,14 +1794,20 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) tv->v_lock = VAR_UNLOCKED; \ } while (0) +static inline void _nothing_conv_empty_dict(typval_T *const tv, + dict_T **const dictp) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(2) +{ + tv_dict_unref(*dictp); + *dictp = NULL; + if (tv != NULL) { + tv->v_lock = VAR_UNLOCKED; + } +} #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ do { \ assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ - tv_dict_unref((dict_T *)dict); \ - *((dict_T **)&dict) = NULL; \ - if (tv != NULL) { \ - ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ - } \ + _nothing_conv_empty_dict(tv, ((dict_T **)&dict)); \ } while (0) static inline int _nothing_conv_real_list_after_start( -- cgit From c8a5d6181b19009e170a3497a30ce35cf288bddf Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Dec 2017 02:39:46 +0300 Subject: *: Fix some problems found during review Still missing: problems in window.c, it should be possible to construct a test for them. --- src/nvim/eval/encode.c | 2 +- src/nvim/eval/typval.c | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 9f16b78976..39897aa9ab 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -209,7 +209,7 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, return false; } len++; - if (TV_LIST_ITEM_TV(li)->vval.v_string != 0) { + if (TV_LIST_ITEM_TV(li)->vval.v_string != NULL) { len += STRLEN(TV_LIST_ITEM_TV(li)->vval.v_string); } }); diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 0c9c4a0347..5040695b09 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -406,9 +406,7 @@ void tv_list_append_list(list_T *const list, list_T *const itemlist) TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; TV_LIST_ITEM_TV(li)->vval.v_list = itemlist; tv_list_append(list, li); - if (itemlist != NULL) { - tv_list_ref(itemlist); - } + tv_list_ref(itemlist); } /// Append a dictionary to a list @@ -510,6 +508,9 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, orig->lv_copylist = copy; } TV_LIST_ITER(orig, item, { + if (got_int) { + break; + } listitem_T *const ni = tv_list_item_alloc(); if (deep) { if (var_item_copy(conv, TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni), @@ -605,6 +606,9 @@ static int list_join_inner(garray_T *const gap, list_T *const l, // Stringify each item in the list. TV_LIST_ITER(l, item, { + if (got_int) { + break; + } char *s; size_t len; s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len); @@ -697,7 +701,7 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, listitem_T *item2 = tv_list_first(l2); for (; item1 != NULL && item2 != NULL ; (item1 = TV_LIST_ITEM_NEXT(l1, item1), - item2 = TV_LIST_ITEM_NEXT(n2, item2))) { + item2 = TV_LIST_ITEM_NEXT(l2, item2))) { if (!tv_equal(TV_LIST_ITEM_TV(item1), TV_LIST_ITEM_TV(item2), ic, recursive)) { return false; @@ -2108,9 +2112,7 @@ void tv_copy(typval_T *const from, typval_T *const to) break; } case VAR_LIST: { - if (from->vval.v_list != NULL) { - tv_list_ref(to->vval.v_list); - } + tv_list_ref(to->vval.v_list); break; } case VAR_DICT: { -- cgit From d5bce42b524708a54243658e87b1e3bd9c7acdf3 Mon Sep 17 00:00:00 2001 From: Michael Schupikov Date: Sat, 23 Sep 2017 09:56:44 +0200 Subject: vim-patch:8.0.0074 Problem: Cannot make Vim fail on an internal error. Solution: Add IEMSG() and IEMSG2(). (Domenique Pelle) Avoid reporting an internal error without mentioning where. https://github.com/vim/vim/commit/95f096030ed1a8afea028f2ea295d6f6a70f466f Signed-off-by: Michael Schupikov --- src/nvim/eval/encode.c | 2 +- src/nvim/eval/typval_encode.c.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index ef647b3ee4..1607d2139a 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -340,7 +340,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, do { \ const char *const fun_ = (const char *)(fun); \ if (fun_ == NULL) { \ - EMSG2(_(e_intern2), "string(): NULL function name"); \ + internal_error("string(): NULL function name"); \ ga_concat(gap, "function(NULL"); \ } else { \ ga_concat(gap, "function("); \ diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index a93ad2dbba..df820f664c 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -600,7 +600,7 @@ _convert_one_value_regular_dict: {} break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()"); + internal_error(STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()"); return FAIL; } } -- cgit From 91d3efa35a26f6c5e58850413ccbb350cb8e7b37 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Dec 2017 11:40:27 +0300 Subject: eval/encode: Avoid unnecessary tv_list_idx_of_item() calls --- src/nvim/eval/encode.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 39897aa9ab..b7af102681 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -136,13 +136,18 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, } case kMPConvPairs: case kMPConvList: { + const int idx = (v.data.l.li == tv_list_first(v.data.l.list) + ? 0 + : (v.data.l.li == NULL + ? tv_list_len(v.data.l.list) - 1 + : (int)tv_list_idx_of_item( + v.data.l.list, + TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)))); const listitem_T *const li = (v.data.l.li == NULL ? tv_list_last(v.data.l.list) : TV_LIST_ITEM_PREV(v.data.l.list, v.data.l.li)); - int idx = (li == NULL - ? 0 - : (int)tv_list_idx_of_item(v.data.l.list, li)); if (v.type == kMPConvList || li == NULL || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST -- cgit From 0c533a488fbe453ae39017a586eff7813da8ed8a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 01:43:42 +0300 Subject: *: Remove most calls to tv_list_item_alloc Still left calls in eval/typval.c and test/unit/eval/helpers.lua. Latter is the only reason why function did not receive `static` modifier. --- src/nvim/eval/decode.c | 40 +++++++++++--------------- src/nvim/eval/typval.c | 76 ++++++++++++++++++++++++++++---------------------- 2 files changed, 60 insertions(+), 56 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index d5c65ebe81..af4e055d23 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -127,9 +127,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, return FAIL; } assert(last_container.special_val == NULL); - listitem_T *obj_li = tv_list_item_alloc(); - *TV_LIST_ITEM_TV(obj_li) = obj.val; - tv_list_append(last_container.container.vval.v_list, obj_li); + tv_list_append_owned_tv(last_container.container.vval.v_list, obj.val); } else if (last_container.stack_index == kv_size(*stack) - 2) { if (!obj.didcolon) { EMSG2(_("E474: Expected colon before dictionary value: %s"), @@ -154,12 +152,8 @@ static inline int json_decoder_pop(ValuesStackItem obj, } else { list_T *const kv_pair = tv_list_alloc(); tv_list_append_list(last_container.special_val, kv_pair); - listitem_T *const key_li = tv_list_item_alloc(); - *TV_LIST_ITEM_TV(key_li) = key.val; - tv_list_append(kv_pair, key_li); - listitem_T *const val_li = tv_list_item_alloc(); - *TV_LIST_ITEM_TV(val_li) = obj.val; - tv_list_append(kv_pair, val_li); + tv_list_append_owned_tv(kv_pair, key.val); + tv_list_append_owned_tv(kv_pair, obj.val); } } else { // Object with key only @@ -1047,10 +1041,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, }; for (size_t i = 0; i < mobj.via.array.size; i++) { - listitem_T *const li = tv_list_item_alloc(); - TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN; - tv_list_append(list, li); - if (msgpack_to_vim(mobj.via.array.ptr[i], TV_LIST_ITEM_TV(li)) + // Not populated yet, need to create list item to push. + tv_list_append_owned_tv(list, (typval_T) { .v_type = VAR_UNKNOWN }); + if (msgpack_to_vim(mobj.via.array.ptr[i], + TV_LIST_ITEM_TV(tv_list_last(list))) == FAIL) { return FAIL; } @@ -1095,20 +1089,20 @@ msgpack_to_vim_generic_map: {} for (size_t i = 0; i < mobj.via.map.size; i++) { list_T *const kv_pair = tv_list_alloc(); tv_list_append_list(list, kv_pair); - listitem_T *const key_li = tv_list_item_alloc(); - TV_LIST_ITEM_TV(key_li)->v_type = VAR_UNKNOWN; - tv_list_append(kv_pair, key_li); - listitem_T *const val_li = tv_list_item_alloc(); - TV_LIST_ITEM_TV(val_li)->v_type = VAR_UNKNOWN; - tv_list_append(kv_pair, val_li); - if (msgpack_to_vim(mobj.via.map.ptr[i].key, TV_LIST_ITEM_TV(key_li)) - == FAIL) { + + typval_T key_tv = { .v_type = VAR_UNKNOWN }; + if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_tv) == FAIL) { + tv_clear(&key_tv); return FAIL; } - if (msgpack_to_vim(mobj.via.map.ptr[i].val, TV_LIST_ITEM_TV(val_li)) - == FAIL) { + tv_list_append_owned_tv(kv_pair, key_tv); + + typval_T val_tv = { .v_type = VAR_UNKNOWN }; + if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_tv) == FAIL) { + tv_clear(&val_tv); return FAIL; } + tv_list_append_owned_tv(kv_pair, val_tv); } break; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 53c56d0ffd..0755507f91 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -393,19 +393,30 @@ void tv_list_append_tv(list_T *const l, typval_T *const tv) tv_list_append(l, li); } +/// Like tv_list_append_tv(), but tv is moved to a list +/// +/// This means that it is no longer valid to use contents of the typval_T after +/// function exits. +void tv_list_append_owned_tv(list_T *const l, typval_T tv) + FUNC_ATTR_NONNULL_ALL +{ + listitem_T *const li = tv_list_item_alloc(); + *TV_LIST_ITEM_TV(li) = tv; + tv_list_append(l, li); +} + /// Append a list to a list as one item /// /// @param[out] l List to append to. /// @param[in,out] itemlist List to append. Reference count is increased. -void tv_list_append_list(list_T *const list, list_T *const itemlist) +void tv_list_append_list(list_T *const l, list_T *const itemlist) FUNC_ATTR_NONNULL_ARG(1) { - listitem_T *const li = tv_list_item_alloc(); - - TV_LIST_ITEM_TV(li)->v_type = VAR_LIST; - TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; - TV_LIST_ITEM_TV(li)->vval.v_list = itemlist; - tv_list_append(list, li); + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval.v_list = itemlist, + }); tv_list_ref(itemlist); } @@ -413,15 +424,14 @@ void tv_list_append_list(list_T *const list, list_T *const itemlist) /// /// @param[out] l List to append to. /// @param[in,out] dict Dictionary to append. Reference count is increased. -void tv_list_append_dict(list_T *const list, dict_T *const dict) +void tv_list_append_dict(list_T *const l, dict_T *const dict) FUNC_ATTR_NONNULL_ARG(1) { - listitem_T *const li = tv_list_item_alloc(); - - TV_LIST_ITEM_TV(li)->v_type = VAR_DICT; - TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; - TV_LIST_ITEM_TV(li)->vval.v_dict = dict; - tv_list_append(list, li); + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval.v_dict = dict, + }); if (dict != NULL) { dict->dv_refcount++; } @@ -438,14 +448,15 @@ void tv_list_append_string(list_T *const l, const char *const str, const ssize_t len) FUNC_ATTR_NONNULL_ARG(1) { - if (str == NULL) { - assert(len == 0 || len == -1); - tv_list_append_allocated_string(l, NULL); - } else { - tv_list_append_allocated_string(l, (len >= 0 - ? xmemdupz(str, (size_t)len) - : xstrdup(str))); - } + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = (str == NULL + ? NULL + : (len >= 0 + ? xmemdupz(str, (size_t)len) + : xstrdup(str))), + }); } /// Append given string to the list @@ -457,12 +468,11 @@ void tv_list_append_string(list_T *const l, const char *const str, void tv_list_append_allocated_string(list_T *const l, char *const str) FUNC_ATTR_NONNULL_ARG(1) { - listitem_T *const li = tv_list_item_alloc(); - - tv_list_append(l, li); - TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; - TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; - TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)str; + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = (char_u *)str, + }); } /// Append number to the list @@ -472,11 +482,11 @@ void tv_list_append_allocated_string(list_T *const l, char *const str) /// listitem_T. void tv_list_append_number(list_T *const l, const varnumber_T n) { - listitem_T *const li = tv_list_item_alloc(); - TV_LIST_ITEM_TV(li)->v_type = VAR_NUMBER; - TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; - TV_LIST_ITEM_TV(li)->vval.v_number = n; - tv_list_append(l, li); + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval.v_number = n, + }); } //{{{2 Operations on the whole list -- cgit From 6bf3dc77c484f757d37c22694e1278abc014278f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 01:52:11 +0300 Subject: eval/typval: Make tv_list_item_alloc static Better write this bit in lua then make reviewers or clint filter out tv_list_item_alloc(). --- src/nvim/eval/typval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 0755507f91..751f1e7aec 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -52,7 +52,7 @@ const char *const tv_empty_string = ""; /// and specifically set lv_lock. /// /// @return [allocated] new list item. -listitem_T *tv_list_item_alloc(void) +static listitem_T *tv_list_item_alloc(void) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC { return xmalloc(sizeof(listitem_T)); -- cgit From 608c3d7baf2745b188bc42f9f45ad1bb188a4828 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 02:11:46 +0300 Subject: eval/typval: Remove tv_list_item_free() as it is unused --- src/nvim/eval/typval.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 751f1e7aec..a49c34e957 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -58,18 +58,6 @@ static listitem_T *tv_list_item_alloc(void) return xmalloc(sizeof(listitem_T)); } -/// Free a list item -/// -/// Also clears the value. Does not touch watchers. -/// -/// @param[out] item Item to free. -void tv_list_item_free(listitem_T *const item) - FUNC_ATTR_NONNULL_ALL -{ - tv_clear(TV_LIST_ITEM_TV(item)); - xfree(item); -} - /// Remove a list item from a List and free it /// /// Also clears the value. @@ -80,7 +68,8 @@ void tv_list_item_remove(list_T *const l, listitem_T *const item) FUNC_ATTR_NONNULL_ALL { tv_list_remove_items(l, item, item); - tv_list_item_free(item); + tv_clear(TV_LIST_ITEM_TV(item)); + xfree(item); } //{{{2 List watchers -- cgit From ac55558c97d02f18b9a99cf2dd279451481c4a2f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 02:41:34 +0300 Subject: eval/typval: Make tv_list_item_remove return pointer to the next item --- src/nvim/eval/typval.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index a49c34e957..4ebe12104f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -64,12 +64,17 @@ static listitem_T *tv_list_item_alloc(void) /// /// @param[out] l List to remove item from. /// @param[in,out] item Item to remove. -void tv_list_item_remove(list_T *const l, listitem_T *const item) +/// +/// @return Pointer to the list item just after removed one, NULL if removed +/// item was the last one. +listitem_T *tv_list_item_remove(list_T *const l, listitem_T *const item) FUNC_ATTR_NONNULL_ALL { + listitem_T *const next_item = TV_LIST_ITEM_NEXT(l, item); tv_list_remove_items(l, item, item); tv_clear(TV_LIST_ITEM_TV(item)); xfree(item); + return next_item; } //{{{2 List watchers -- cgit From 67fa9e5237526b092c1ac672504e259db7f14126 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 16:38:30 +0300 Subject: eval: Rename tv_list_remove_items() to tv_list_drop_items() tv_list_remove_items() may cause confusion with tv_list_item_remove() --- src/nvim/eval/typval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4ebe12104f..4f717250cd 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -71,7 +71,7 @@ listitem_T *tv_list_item_remove(list_T *const l, listitem_T *const item) FUNC_ATTR_NONNULL_ALL { listitem_T *const next_item = TV_LIST_ITEM_NEXT(l, item); - tv_list_remove_items(l, item, item); + tv_list_drop_items(l, item, item); tv_clear(TV_LIST_ITEM_TV(item)); xfree(item); return next_item; @@ -261,8 +261,8 @@ void tv_list_unref(list_T *const l) /// @param[out] l List to remove from. /// @param[in] item First item to remove. /// @param[in] item2 Last item to remove. -void tv_list_remove_items(list_T *const l, listitem_T *const item, - listitem_T *const item2) +void tv_list_drop_items(list_T *const l, listitem_T *const item, + listitem_T *const item2) FUNC_ATTR_NONNULL_ALL { // Notify watchers. @@ -296,7 +296,7 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, const int cnt) FUNC_ATTR_NONNULL_ALL { - tv_list_remove_items(l, item, item2); + tv_list_drop_items(l, item, item2); item->li_prev = tgt_l->lv_last; item2->li_next = NULL; if (tgt_l->lv_last == NULL) { -- cgit From 7997147245da8c9e9fca4e1862d71bd6b28e1c06 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 17:52:04 +0300 Subject: eval: Replace some tv_list_item_remove() calls There is nothing wrong with them, just it is generally better to remove a range then to remove items individually. --- src/nvim/eval/typval.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4f717250cd..5c9005595d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -284,6 +284,23 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item, l->lv_idx_item = NULL; } +/// Like tv_list_drop_items, but also frees all removed items +void tv_list_remove_items(list_T *const l, listitem_T *const item, + listitem_T *const item2) + FUNC_ATTR_NONNULL_ALL +{ + tv_list_drop_items(l, item, item2); + for(listitem_T *li = item;;) { + tv_clear(TV_LIST_ITEM_TV(li)); + listitem_T *const nli = li->li_next; + xfree(li); + if (li == item2) { + break; + } + li = nli; + } +} + /// Move items "item" to "item2" from list "l" to the end of the list "tgt_l" /// /// @param[out] l List to move from. -- cgit From bc52ec61105160eb2b648af239e44cc594529fbf Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Dec 2017 23:09:26 +0300 Subject: *: Fix linter errors --- src/nvim/eval/typval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 5c9005595d..fba9e9c843 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -290,7 +290,7 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item, FUNC_ATTR_NONNULL_ALL { tv_list_drop_items(l, item, item2); - for(listitem_T *li = item;;) { + for (listitem_T *li = item;;) { tv_clear(TV_LIST_ITEM_TV(li)); listitem_T *const nli = li->li_next; xfree(li); -- cgit From b6ee90a2433276175462b81106378009b4893e04 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Dec 2017 01:44:44 +0300 Subject: eval: Refactor some potentially dangerous list appends --- src/nvim/eval/typval.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/nvim/eval/typval.h | 22 ++++++++-------------- 2 files changed, 50 insertions(+), 14 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index fba9e9c843..21bb84a945 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -757,6 +758,47 @@ void tv_list_reverse(list_T *const l) l->lv_idx = l->lv_len - l->lv_idx - 1; } +// FIXME Add unit tests for tv_list_item_sort(). + +/// Sort list using libc qsort +/// +/// @param[in,out] l List to sort, will be sorted in-place. +/// @param ptrs Preallocated array of items to sort, must have at least +/// tv_list_len(l) entries. Should not be initialized. +/// @param[in] item_compare_func Function used to compare list items. +/// @param errp Location where information about whether error occurred is +/// saved by item_compare_func. If boolean there appears to be +/// true list will not be modified. Must be initialized to false +/// by the caller. +void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs, + const ListSorter item_compare_func, + bool *errp) + FUNC_ATTR_NONNULL_ARG(3, 4) +{ + const int len = tv_list_len(l); + if (len <= 1) { + return; + } + int i = 0; + TV_LIST_ITER(l, li, { + ptrs[i].item = li; + ptrs[i].idx = i; + i++; + }); + // Sort the array with item pointers. + qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func); + if (!(*errp)) { + // Clear the list and append the items in the sorted order. + l->lv_first = NULL; + l->lv_last = NULL; + l->lv_idx_item = NULL; + l->lv_len = 0; + for (i = 0; i < len; i++) { + tv_list_append(l, ptrs[i].item); + } + } +} + //{{{2 Indexing/searching /// Locate item with a given index in a list and return it diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 2bce7bd6b2..c9a9a3e7e8 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -296,6 +296,14 @@ typedef struct list_stack_S { struct list_stack_S *prev; } list_stack_T; +/// Structure representing one list item, used for sort array. +typedef struct { + listitem_T *item; ///< Sorted list item. + int idx; ///< Sorted list item index. +} ListSortItem; + +typedef int (*ListSorter)(const void *, const void *); + // In a hashtab item "hi_key" points to "di_key" in a dictitem. // This avoids adding a pointer to the hashtab item. @@ -403,20 +411,6 @@ static inline list_T *tv_list_latest_copy(const list_T *const l) return l->lv_copylist; } -/// Clear the list without freeing anything at all -/// -/// For use in sort() which saves items to a separate array and readds them back -/// after sorting via a number of tv_list_append() calls. -/// -/// @param[out] l List to clear. -static inline void tv_list_clear(list_T *const l) -{ - l->lv_first = NULL; - l->lv_last = NULL; - l->lv_idx_item = NULL; - l->lv_len = 0; -} - static inline int tv_list_uidx(const list_T *const l, int n) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; -- cgit From dea7a41138674b45e8cfd5c1d713d4048987c830 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Sat, 30 Dec 2017 22:17:31 -0800 Subject: Add another const to tv_copy Clang static analyzer had trouble with filter_map in eval.c because tv_copy could, in principle, change the v_type of argvars[0]. It saw a potential null pointer going somewhere it shouldn't as a result. The from argument in tv_copy should be const, which also cleans up the static analyzer's complaint. --- src/nvim/eval/typval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 21bb84a945..ac6c8c8aa6 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2153,7 +2153,7 @@ void tv_free(typval_T *tv) /// /// @param[in] from Location to copy from. /// @param[out] to Location to copy to. -void tv_copy(typval_T *const from, typval_T *const to) +void tv_copy(const typval_T *const from, typval_T *const to) { to->v_type = from->v_type; to->v_lock = VAR_UNLOCKED; -- cgit From 18d244eded434e6bc47b351ef00088378883bf1d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 11 Jan 2018 01:32:41 +0100 Subject: coverity/169163: decode_string: Null pointer deref *** CID 169163: Null pointer dereferences (FORWARD_NULL) /src/nvim/eval/decode.c: 290 in decode_string() 284 if (elw_ret == -1) { 285 tv_clear(&tv); 286 return (typval_T) { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; 287 } 288 return tv; 289 } else { >>> CID 169163: Null pointer dereferences (FORWARD_NULL) >>> Passing null pointer "s" to "xmemdupz", which dereferences it. (The dereference is assumed on the basis of the 'nonnull' parameter attribute.) 290 return (typval_T) { 291 .v_type = VAR_STRING, 292 .v_lock = VAR_UNLOCKED, 293 .vval = { .v_string = (char_u *)( 294 s_allocated ? (char *)s : xmemdupz(s, len)) }, 295 }; --- src/nvim/eval/decode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index af4e055d23..cd967ed5c5 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -285,7 +285,7 @@ typval_T decode_string(const char *const s, const size_t len, .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, .vval = { .v_string = (char_u *)( - s_allocated ? (char *)s : xmemdupz(s, len)) }, + (s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) }, }; } } -- cgit From 9ea1752d60589e8fc5e7184144bc6d1c1b9f16a3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Jan 2018 00:00:16 +0300 Subject: *: Provide list length when allocating lists --- src/nvim/eval/decode.c | 32 +++++++++++++++++++------------- src/nvim/eval/typval.c | 18 ++++++++++++++---- src/nvim/eval/typval.h | 19 +++++++++++++++++++ 3 files changed, 52 insertions(+), 17 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index af4e055d23..c729d00ae7 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -150,7 +150,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, } obj_di->di_tv = obj.val; } else { - list_T *const kv_pair = tv_list_alloc(); + list_T *const kv_pair = tv_list_alloc(2); tv_list_append_list(last_container.special_val, kv_pair); tv_list_append_owned_tv(kv_pair, key.val); tv_list_append_owned_tv(kv_pair, obj.val); @@ -221,13 +221,18 @@ static inline int json_decoder_pop(ValuesStackItem obj, /// Create a new special dictionary that ought to represent a MAP /// /// @param[out] ret_tv Address where new special dictionary is saved. +/// @param[in] len Expected number of items to be populated before list +/// becomes accessible from VimL. It is still valid to +/// underpopulate a list, value only controls how many elements +/// will be allocated in advance. @see ListLenSpecials. /// /// @return [allocated] list which should contain key-value pairs. Return value /// may be safely ignored. -list_T *decode_create_map_special_dict(typval_T *const ret_tv) +list_T *decode_create_map_special_dict(typval_T *const ret_tv, + const ptrdiff_t len) FUNC_ATTR_NONNULL_ALL { - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc(len); tv_list_ref(list); create_special_dict(ret_tv, kMPMap, ((typval_T) { .v_type = VAR_LIST, @@ -263,7 +268,7 @@ typval_T decode_string(const char *const s, const size_t len, ? ((s != NULL) && (memchr(s, NUL, len) != NULL)) : (bool)hasnul); if (really_hasnul) { - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc(kListLenMayKnow); tv_list_ref(list); typval_T tv; create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) { @@ -843,7 +848,7 @@ json_decode_string_cycle_start: break; } case '[': { - list_T *list = tv_list_alloc(); + list_T *list = tv_list_alloc(kListLenMayKnow); tv_list_ref(list); typval_T tv = { .v_type = VAR_LIST, @@ -864,7 +869,7 @@ json_decode_string_cycle_start: list_T *val_list = NULL; if (next_map_special) { next_map_special = false; - val_list = decode_create_map_special_dict(&tv); + val_list = decode_create_map_special_dict(&tv, kListLenMayKnow); } else { dict_T *dict = tv_dict_alloc(); dict->dv_refcount++; @@ -964,7 +969,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.u64 }, }; } else { - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc(4); tv_list_ref(list); create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, @@ -987,7 +992,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.i64 }, }; } else { - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc(4); tv_list_ref(list); create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, @@ -1033,7 +1038,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_ARRAY: { - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc((ptrdiff_t)mobj.via.array.size); tv_list_ref(list); *rettv = (typval_T) { .v_type = VAR_LIST, @@ -1085,9 +1090,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; msgpack_to_vim_generic_map: {} - list_T *const list = decode_create_map_special_dict(rettv); + list_T *const list = decode_create_map_special_dict( + rettv, (ptrdiff_t)mobj.via.map.size); for (size_t i = 0; i < mobj.via.map.size; i++) { - list_T *const kv_pair = tv_list_alloc(); + list_T *const kv_pair = tv_list_alloc(2); tv_list_append_list(list, kv_pair); typval_T key_tv = { .v_type = VAR_UNKNOWN }; @@ -1107,10 +1113,10 @@ msgpack_to_vim_generic_map: {} break; } case MSGPACK_OBJECT_EXT: { - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc(2); tv_list_ref(list); tv_list_append_number(list, mobj.via.ext.type); - list_T *const ext_val_list = tv_list_alloc(); + list_T *const ext_val_list = tv_list_alloc(kListLenMayKnow); tv_list_append_list(list, ext_val_list); create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST, diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ac6c8c8aa6..6f829be0b4 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -132,8 +132,14 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item) /// /// Caller should take care of the reference count. /// +/// @param[in] len Expected number of items to be populated before list +/// becomes accessible from VimL. It is still valid to +/// underpopulate a list, value only controls how many elements +/// will be allocated in advance. Currently does nothing. +/// @see ListLenSpecials. +/// /// @return [allocated] new list. -list_T *tv_list_alloc(void) +list_T *tv_list_alloc(const ptrdiff_t len) FUNC_ATTR_NONNULL_RET { list_T *const list = xcalloc(1, sizeof(list_T)); @@ -521,7 +527,7 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, return NULL; } - list_T *copy = tv_list_alloc(); + list_T *copy = tv_list_alloc(tv_list_len(orig)); tv_list_ref(copy); if (copyID != 0) { // Do this before adding the items, because one of the items may @@ -1817,12 +1823,16 @@ void tv_dict_set_keys_readonly(dict_T *const dict) /// Also sets reference count. /// /// @param[out] ret_tv Structure where list is saved. +/// @param[in] len Expected number of items to be populated before list +/// becomes accessible from VimL. It is still valid to +/// underpopulate a list, value only controls how many elements +/// will be allocated in advance. @see ListLenSpecials. /// /// @return [allocated] pointer to the created list. -list_T *tv_list_alloc_ret(typval_T *const ret_tv) +list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len) FUNC_ATTR_NONNULL_ALL { - list_T *const l = tv_list_alloc(); + list_T *const l = tv_list_alloc(len); ret_tv->vval.v_list = l; ret_tv->v_type = VAR_LIST; ret_tv->v_lock = VAR_UNLOCKED; diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index c9a9a3e7e8..3993a384d2 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -31,6 +31,25 @@ typedef double float_T; /// Refcount for dict or list that should not be freed enum { DO_NOT_FREE_CNT = (INT_MAX / 2) }; +/// Additional values for tv_list_alloc() len argument +enum { + /// List length is not known in advance + /// + /// To be used when there is neither a way to know how many elements will be + /// needed nor are any educated guesses. + kListLenUnknown = -1, + /// List length *should* be known, but is actually not + /// + /// All occurrences of this value should be eventually removed. This is for + /// the case when the only reason why list length is not known is that it + /// would be hard to code without refactoring, but refactoring is needed. + kListLenShouldKnow = -2, + /// List length may be known in advance, but it requires too much effort + /// + /// To be used when it looks impractical to determine list length. + kListLenMayKnow = -3, +} ListLenSpecials; + /// Maximal possible value of varnumber_T variable #define VARNUMBER_MAX INT64_MAX #define UVARNUMBER_MAX UINT64_MAX -- cgit From 6a1557f2f496bf8e8f4dad7ed0d423051a7b65e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Jan 2018 04:25:42 +0300 Subject: eval/typval: Log list actions New logging is guarded by cmake LOG_LIST_ACTIONS define. To make it more efficient it is allocated as a linked list with chunks of length 2^(7+chunk_num); that uses basically the same idea as behind increasing kvec length (make appending O(1) (amortized)), but reduces constant by not bothering to move memory around what realloc() would surely do: it is not like we need random access to log entries here to justify usage of a single continuous memory block. --- src/nvim/eval/typval.c | 81 +++++++++++++++++++++++++++++++++++++++++ src/nvim/eval/typval.h | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 6f829be0b4..c8b550f902 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -31,6 +31,7 @@ #include "nvim/message.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck +#include "nvim/os/fileio.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.c.generated.h" @@ -45,6 +46,70 @@ bool tv_in_free_unref_items = false; const char *const tv_empty_string = ""; //{{{1 Lists +//{{{2 List log +#ifdef LOG_LIST_ACTIONS +ListLog *list_log_first = NULL; +ListLog *list_log_last = NULL; + +/// Write list log to the given file +/// +/// @param[in] fname File to write log to. Will be appended to if already +/// present. +void list_write_log(const char *const fname) + FUNC_ATTR_NONNULL_ALL +{ + FileDescriptor fp; + const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600); + if (fo_ret != 0) { + emsgf(_("E5142: Failed to open file %s: %s"), fname, os_strerror(fo_ret)); + return; + } + for (ListLog *chunk = list_log_first; chunk != NULL;) { + for (size_t i = 0; i < chunk->size; i++) { + char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2]; + // act : hex " c:" len "[]" "\n\0" + const ListLogEntry entry = chunk->entries[i]; + const size_t snp_len = (size_t)snprintf( + buf, sizeof(buf), + "%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR + "\n", + entry.action, entry.l, entry.len, entry.li1, entry.li2); + assert(snp_len + 1 == sizeof(buf)); + const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len); + if (fw_ret != (ptrdiff_t)snp_len) { + assert(fw_ret < 0); + if (i) { + memmove(chunk->entries, chunk->entries + i, + sizeof(chunk->entries[0]) * (chunk->size - i)); + chunk->size -= i; + } + emsgf(_("E5143: Failed to write to file %s: %s"), + fname, os_strerror((int)fw_ret)); + return; + } + } + list_log_first = chunk->next; + xfree(chunk); + chunk = list_log_first; + } + const int fc_ret = file_close(&fp, true); + if (fc_ret != 0) { + emsgf(_("E5144: Failed to close file %s: %s"), fname, os_strerror(fc_ret)); + } +} + +#ifdef EXITFREE +/// Free list log +void list_free_log(void) +{ + for (ListLog *chunk = list_log_first; chunk != NULL;) { + list_log_first = chunk->next; + xfree(chunk); + chunk = list_log_first; + } +} +#endif +#endif //{{{2 List item /// Allocate a list item @@ -151,6 +216,7 @@ list_T *tv_list_alloc(const ptrdiff_t len) list->lv_used_prev = NULL; list->lv_used_next = gc_first_list; gc_first_list = list; + list_log(list, NULL, (void *)(uintptr_t)len, "alloc"); return list; } @@ -180,6 +246,8 @@ void tv_list_init_static10(staticList10_T *const sl) li->li_prev = li - 1; li->li_next = li + 1; } + list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1], + "s10init"); #undef SL_SIZE } @@ -191,6 +259,7 @@ void tv_list_init_static(list_T *const l) { memset(l, 0, sizeof(*l)); l->lv_refcount = DO_NOT_FREE_CNT; + list_log(l, NULL, NULL, "sinit"); } /// Free items contained in a list @@ -199,6 +268,7 @@ void tv_list_init_static(list_T *const l) void tv_list_free_contents(list_T *const l) FUNC_ATTR_NONNULL_ALL { + list_log(l, NULL, NULL, "freecont"); for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) { // Remove the item before deleting it. l->lv_first = item->li_next; @@ -228,6 +298,7 @@ void tv_list_free_list(list_T *const l) if (l->lv_used_next != NULL) { l->lv_used_next->lv_used_prev = l->lv_used_prev; } + list_log(l, NULL, NULL, "freelist"); xfree(l); } @@ -272,6 +343,7 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item, listitem_T *const item2) FUNC_ATTR_NONNULL_ALL { + list_log(l, item, item2, "drop"); // Notify watchers. for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) { l->lv_len--; @@ -289,6 +361,7 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item, item->li_prev->li_next = item2->li_next; } l->lv_idx_item = NULL; + list_log(l, l->lv_first, l->lv_last, "afterdrop"); } /// Like tv_list_drop_items, but also frees all removed items @@ -296,6 +369,7 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2) FUNC_ATTR_NONNULL_ALL { + list_log(l, item, item2, "remove"); tv_list_drop_items(l, item, item2); for (listitem_T *li = item;;) { tv_clear(TV_LIST_ITEM_TV(li)); @@ -320,6 +394,7 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, const int cnt) FUNC_ATTR_NONNULL_ALL { + list_log(l, item, item2, "move"); tv_list_drop_items(l, item, item2); item->li_prev = tgt_l->lv_last; item2->li_next = NULL; @@ -330,6 +405,7 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, } tgt_l->lv_last = item2; tgt_l->lv_len += cnt; + list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt"); } /// Insert list item @@ -358,6 +434,7 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, } item->li_prev = ni; l->lv_len++; + list_log(l, ni, item, "insert"); } } @@ -384,6 +461,7 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv, void tv_list_append(list_T *const l, listitem_T *const item) FUNC_ATTR_NONNULL_ALL { + list_log(l, item, NULL, "append"); if (l->lv_last == NULL) { // empty list l->lv_first = item; @@ -747,6 +825,7 @@ void tv_list_reverse(list_T *const l) if (tv_list_len(l) <= 1) { return; } + list_log(l, NULL, NULL, "reverse"); #define SWAP(a, b) \ do { \ tmp = a; \ @@ -785,6 +864,7 @@ void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs, if (len <= 1) { return; } + list_log(l, NULL, NULL, "sort"); int i = 0; TV_LIST_ITER(l, li, { ptrs[i].item = li; @@ -873,6 +953,7 @@ listitem_T *tv_list_find(list_T *const l, int n) // Cache the used index. l->lv_idx = idx; l->lv_idx_item = item; + list_log(l, l->lv_idx_item, (void *)(uintptr_t)l->lv_idx, "find"); return item; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3993a384d2..40a1738d9e 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -20,6 +20,9 @@ #include "nvim/gettext.h" #include "nvim/message.h" #include "nvim/macros.h" +#ifdef LOG_LIST_ACTIONS +# include "nvim/memory.h" +#endif /// Type used for VimL VAR_NUMBER values typedef int64_t varnumber_T; @@ -323,6 +326,96 @@ typedef struct { typedef int (*ListSorter)(const void *, const void *); +#ifdef LOG_LIST_ACTIONS + +/// List actions log entry +typedef struct { + uintptr_t l; ///< List log entry belongs to. + uintptr_t li1; ///< First list item log entry belongs to, if applicable. + uintptr_t li2; ///< Second list item log entry belongs to, if applicable. + int len; ///< List length when log entry was created. + const char *action; ///< Logged action. +} ListLogEntry; + +typedef struct list_log ListLog; + +/// List actions log +struct list_log { + ListLog *next; ///< Next chunk or NULL. + size_t capacity; ///< Number of entries in current chunk. + size_t size; ///< Current chunk size. + ListLogEntry entries[]; ///< Actual log entries. +}; + +extern ListLog *list_log_first; ///< First list log chunk, NULL if missing +extern ListLog *list_log_last; ///< Last list log chunk + +static inline ListLog *list_log_alloc(const size_t size) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Allocate a new log chunk and update globals +/// +/// @param[in] size Number of entries in a new chunk. +/// +/// @return [allocated] Newly allocated chunk. +static inline ListLog *list_log_new(const size_t size) +{ + ListLog *ret = xmalloc(offsetof(ListLog, entries) + + size * sizeof(ret->entries[0])); + ret->size = 0; + ret->capacity = size; + ret->next = NULL; + if (list_log_first == NULL) { + list_log_first = ret; + } else { + list_log_last->next = ret; + } + list_log_last = ret; + return ret; +} + +static inline void list_log(const list_T *const l, + const listitem_T *const li1, + const listitem_T *const li2, + const char *const action) + REAL_FATTR_ALWAYS_INLINE; + +/// Add new entry to log +/// +/// If last chunk was filled it uses twice as much memory to allocate the next +/// chunk. +/// +/// @param[in] l List to which entry belongs. +/// @param[in] li1 List item 1. +/// @param[in] li2 List item 2, often used for integers and not list items. +/// @param[in] action Logged action. +static inline void list_log(const list_T *const l, + const listitem_T *const li1, + const listitem_T *const li2, + const char *const action) +{ + ListLog *tgt; + if (list_log_first == NULL) { + tgt = list_log_new(128); + } else if (list_log_last->size == list_log_last->capacity) { + tgt = list_log_new(list_log_last->capacity * 2); + } else { + tgt = list_log_last; + } + tgt->entries[tgt->size++] = (ListLogEntry) { + .l = (uintptr_t)l, + .li1 = (uintptr_t)li1, + .li2 = (uintptr_t)li2, + .len = (l == NULL ? 0 : l->lv_len), + .action = action, + }; +} +#else +# define list_log(...) +# define list_write_log(...) +# define list_free_log() +#endif + // In a hashtab item "hi_key" points to "di_key" in a dictitem. // This avoids adding a pointer to the hashtab item. @@ -396,6 +489,7 @@ static inline int tv_list_len(const list_T *const l) /// @param[in] l List to check. static inline int tv_list_len(const list_T *const l) { + list_log(l, NULL, NULL, "len"); if (l == NULL) { return 0; } @@ -479,8 +573,10 @@ static inline listitem_T *tv_list_first(const list_T *const l) static inline listitem_T *tv_list_first(const list_T *const l) { if (l == NULL) { + list_log(l, NULL, NULL, "first"); return NULL; } + list_log(l, l->lv_first, NULL, "first"); return l->lv_first; } @@ -495,8 +591,10 @@ static inline listitem_T *tv_list_last(const list_T *const l) static inline listitem_T *tv_list_last(const list_T *const l) { if (l == NULL) { + list_log(l, NULL, NULL, "last"); return NULL; } + list_log(l, l->lv_last, NULL, "last"); return l->lv_last; } @@ -564,6 +662,7 @@ extern bool tv_in_free_unref_items; #define _TV_LIST_ITER_MOD(modifier, l, li, code) \ do { \ modifier list_T *const l_ = (l); \ + list_log(l_, NULL, NULL, "iter" #modifier); \ if (l_ != NULL) { \ for (modifier listitem_T *li = l_->lv_first; \ li != NULL; li = li->li_next) { \ -- cgit From 43833af53c0ee5cc230dae49f69d0593ff5edf8f Mon Sep 17 00:00:00 2001 From: George Zhao Date: Wed, 17 Jan 2018 19:45:49 +0800 Subject: Fix warning about math functions, include isnan, isinf, fpclassify. --- src/nvim/eval/encode.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 31779a544f..f6c42a2d3c 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -288,6 +288,11 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, : OK); } +#ifdef __MINGW32__ +# undef fpclassify +# define fpclassify __fpclassify +#endif + #define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ do { \ const char *const buf_ = (const char *) buf; \ -- cgit From 26b84a8b3e99d9dce2ad9f34e1663f119cd1c558 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 16 Mar 2018 07:34:27 +0100 Subject: build/MSVC: fix "C4003: not enough actual parameters for macro" For the case of TV_DICTITEM_STRUCT, we can't just pass `1` because: https://github.com/neovim/neovim/pull/8142#discussion_r175262436 > this variant will trigger array overrun warnings from various static analyzers. --- src/nvim/eval/typval.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 40a1738d9e..2272a580d6 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -206,11 +206,11 @@ struct dictitem_S { char_u di_key[1]; ///< key (actually longer!) }; -#define TV_DICTITEM_STRUCT(KEY_LEN) \ +#define TV_DICTITEM_STRUCT(...) \ struct { \ typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ uint8_t di_flags; /* Flags. */ \ - char_u di_key[KEY_LEN]; /* Key value. */ \ + char_u di_key[__VA_ARGS__]; /* Key value. */ \ } /// Structure to hold a scope dictionary -- cgit