aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/eval/typval.c23
-rw-r--r--test/unit/eval/typval_spec.lua32
2 files changed, 54 insertions, 1 deletions
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
@@ -1387,11 +1387,32 @@ int tv_dict_add_str(dict_T *const d,
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;
diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua
index 5d543f914f..33d5bb28be 100644
--- a/test/unit/eval/typval_spec.lua
+++ b/test/unit/eval/typval_spec.lua
@@ -1963,6 +1963,38 @@ describe('typval.c', function()
alloc_log:check({})
end)
end)
+ describe('allocated_str()', function()
+ itp('works', function()
+ local d = dict({test=10})
+ eq({test=10}, dct2tbl(d))
+ alloc_log:clear()
+ local s1 = lib.xstrdup('TEST')
+ local s2 = lib.xstrdup('TEST')
+ local s3 = lib.xstrdup('TEST')
+ alloc_log:check({
+ a.str(s1, 'TEST'),
+ a.str(s2, 'TEST'),
+ a.str(s3, 'TEST'),
+ })
+ eq(OK, lib.tv_dict_add_allocated_str(d, 'testt', 3, s1))
+ local dis = dict_items(d)
+ alloc_log:check({
+ a.di(dis.tes, 'tes'),
+ })
+ eq({test=10, tes='TEST'}, dct2tbl(d))
+ eq(FAIL, check_emsg(function() return lib.tv_dict_add_allocated_str(d, 'testt', 3, s2) end,
+ 'E685: Internal error: hash_add()'))
+ alloc_log:clear()
+ lib.emsg_skip = lib.emsg_skip + 1
+ eq(FAIL, check_emsg(function() return lib.tv_dict_add_allocated_str(d, 'testt', 3, s3) end,
+ nil))
+ lib.emsg_skip = lib.emsg_skip - 1
+ alloc_log:clear_tmp_allocs()
+ alloc_log:check({
+ a.freed(s3),
+ })
+ end)
+ end)
end)
describe('clear()', function()
itp('works', function()