aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api/private/converter.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/api/private/converter.c')
-rw-r--r--src/nvim/api/private/converter.c121
1 files changed, 56 insertions, 65 deletions
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index 90023171e5..a70ef1e50b 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -11,7 +11,6 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/func_attr.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
#include "nvim/types_defs.h"
@@ -20,6 +19,8 @@
/// Helper structure for vim_to_object
typedef struct {
kvec_withinit_t(Object, 2) stack; ///< Object stack.
+ Arena *arena; ///< arena where objects will be allocated
+ bool reuse_strdata;
} EncodedData;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -42,12 +43,21 @@ typedef struct {
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
kvi_push(edata->stack, FLOAT_OBJ((Float)(flt)))
+static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t len)
+{
+ if (edata->reuse_strdata) {
+ return STRING_OBJ(cbuf_as_string((char *)(len ? data : ""), len));
+ } else {
+ return CBUF_TO_ARENA_OBJ(edata->arena, data, len);
+ }
+}
+
#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
do { \
const size_t len_ = (size_t)(len); \
const char *const str_ = (str); \
assert(len_ == 0 || str_ != NULL); \
- kvi_push(edata->stack, STRING_OBJ(cbuf_to_string((len_ ? str_ : ""), len_))); \
+ kvi_push(edata->stack, typval_cbuf_to_obj(edata, str_, len_)); \
} while (0)
#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING
@@ -59,10 +69,7 @@ typedef struct {
do { \
const size_t len_ = (size_t)(len); \
const blob_T *const blob_ = (blob); \
- kvi_push(edata->stack, STRING_OBJ(((String) { \
- .data = len_ != 0 ? xmemdupz(blob_->bv_ga.ga_data, len_) : xstrdup(""), \
- .size = len_ \
- }))); \
+ kvi_push(edata->stack, typval_cbuf_to_obj(edata, len_ ? blob_->bv_ga.ga_data : "", len_)); \
} while (0)
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
@@ -91,11 +98,7 @@ typedef struct {
static inline void typval_encode_list_start(EncodedData *const edata, const size_t len)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
{
- kvi_push(edata->stack, ARRAY_OBJ(((Array) {
- .capacity = len,
- .size = 0,
- .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.array.items)),
- })));
+ kvi_push(edata->stack, ARRAY_OBJ(arena_array(edata->arena, len)));
}
#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
@@ -110,7 +113,7 @@ static inline void typval_encode_between_list_items(EncodedData *const edata)
Object *const list = &kv_last(edata->stack);
assert(list->type == kObjectTypeArray);
assert(list->data.array.size < list->data.array.capacity);
- list->data.array.items[list->data.array.size++] = item;
+ ADD_C(list->data.array, item);
}
#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
@@ -132,11 +135,7 @@ static inline void typval_encode_list_end(EncodedData *const edata)
static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
{
- kvi_push(edata->stack, DICTIONARY_OBJ(((Dictionary) {
- .capacity = len,
- .size = 0,
- .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.dictionary.items)),
- })));
+ kvi_push(edata->stack, DICTIONARY_OBJ(arena_dict(edata->arena, len)));
}
#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
@@ -157,9 +156,8 @@ static inline void typval_encode_after_key(EncodedData *const edata)
dict->data.dictionary.items[dict->data.dictionary.size].key
= key.data.string;
} else {
- api_free_object(key);
dict->data.dictionary.items[dict->data.dictionary.size].key
- = STATIC_CSTR_TO_STRING("__INVALID_KEY__");
+ = STATIC_CSTR_AS_STRING("__INVALID_KEY__");
}
}
@@ -234,17 +232,22 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
#undef TYPVAL_ENCODE_CONV_RECURSE
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
-/// Convert a vim object to an `Object` instance, recursively expanding
+/// Convert a vim object to an `Object` instance, recursively converting
/// Arrays/Dictionaries.
///
/// @param obj The source object
+/// @param arena if NULL, use direct allocation
+/// @param reuse_strdata when true, don't copy string data to Arena but reference
+/// typval strings directly. takes no effect when arena is
+/// NULL
/// @return The converted value
-Object vim_to_object(typval_T *obj)
+Object vim_to_object(typval_T *obj, Arena *arena, bool reuse_strdata)
{
EncodedData edata;
kvi_init(edata.stack);
- const int evo_ret = encode_vim_to_object(&edata, obj,
- "vim_to_object argument");
+ edata.arena = arena;
+ edata.reuse_strdata = reuse_strdata;
+ const int evo_ret = encode_vim_to_object(&edata, obj, "vim_to_object argument");
(void)evo_ret;
assert(evo_ret == OK);
Object ret = kv_A(edata.stack, 0);
@@ -259,14 +262,20 @@ Object vim_to_object(typval_T *obj)
/// @param tv Conversion result is placed here. On failure member v_type is
/// set to VAR_UNKNOWN (no allocation was made for this variable).
/// @param err Error object.
+void object_to_vim(Object obj, typval_T *tv, Error *err)
+{
+ object_to_vim_take_luaref(&obj, tv, false, err);
+}
+
+/// same as object_to_vim but consumes all luarefs (nested) in `obj`
///
-/// @returns true if conversion is successful, otherwise false.
-bool object_to_vim(Object obj, typval_T *tv, Error *err)
+/// useful when `obj` is allocated on an arena
+void object_to_vim_take_luaref(Object *obj, typval_T *tv, bool take_luaref, Error *err)
{
tv->v_type = VAR_UNKNOWN;
tv->v_lock = VAR_UNLOCKED;
- switch (obj.type) {
+ switch (obj->type) {
case kObjectTypeNil:
tv->v_type = VAR_SPECIAL;
tv->vval.v_special = kSpecialVarNull;
@@ -274,46 +283,40 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
case kObjectTypeBoolean:
tv->v_type = VAR_BOOL;
- tv->vval.v_bool = obj.data.boolean ? kBoolVarTrue : kBoolVarFalse;
+ tv->vval.v_bool = obj->data.boolean ? kBoolVarTrue : kBoolVarFalse;
break;
case kObjectTypeBuffer:
case kObjectTypeWindow:
case kObjectTypeTabpage:
case kObjectTypeInteger:
- STATIC_ASSERT(sizeof(obj.data.integer) <= sizeof(varnumber_T),
+ STATIC_ASSERT(sizeof(obj->data.integer) <= sizeof(varnumber_T),
"Integer size must be <= Vimscript number size");
tv->v_type = VAR_NUMBER;
- tv->vval.v_number = (varnumber_T)obj.data.integer;
+ tv->vval.v_number = (varnumber_T)obj->data.integer;
break;
case kObjectTypeFloat:
tv->v_type = VAR_FLOAT;
- tv->vval.v_float = obj.data.floating;
+ tv->vval.v_float = obj->data.floating;
break;
case kObjectTypeString:
tv->v_type = VAR_STRING;
- if (obj.data.string.data == NULL) {
+ if (obj->data.string.data == NULL) {
tv->vval.v_string = NULL;
} else {
- tv->vval.v_string = xmemdupz(obj.data.string.data,
- obj.data.string.size);
+ tv->vval.v_string = xmemdupz(obj->data.string.data,
+ obj->data.string.size);
}
break;
case kObjectTypeArray: {
- list_T *const list = tv_list_alloc((ptrdiff_t)obj.data.array.size);
+ list_T *const list = tv_list_alloc((ptrdiff_t)obj->data.array.size);
- for (uint32_t i = 0; i < obj.data.array.size; i++) {
- Object item = obj.data.array.items[i];
+ for (uint32_t i = 0; i < obj->data.array.size; i++) {
typval_T li_tv;
-
- if (!object_to_vim(item, &li_tv, err)) {
- tv_list_free(list);
- return false;
- }
-
+ object_to_vim_take_luaref(&obj->data.array.items[i], &li_tv, take_luaref, err);
tv_list_append_owned_tv(list, li_tv);
}
tv_list_ref(list);
@@ -326,27 +329,11 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
case kObjectTypeDictionary: {
dict_T *const dict = tv_dict_alloc();
- for (uint32_t i = 0; i < obj.data.dictionary.size; i++) {
- KeyValuePair item = obj.data.dictionary.items[i];
- String key = item.key;
-
- if (key.size == 0) {
- api_set_error(err, kErrorTypeValidation,
- "Empty dictionary keys aren't allowed");
- // cleanup
- tv_dict_free(dict);
- return false;
- }
-
+ for (uint32_t i = 0; i < obj->data.dictionary.size; i++) {
+ KeyValuePair *item = &obj->data.dictionary.items[i];
+ String key = item->key;
dictitem_T *const di = tv_dict_item_alloc(key.data);
-
- if (!object_to_vim(item.value, &di->di_tv, err)) {
- // cleanup
- tv_dict_item_free(di);
- tv_dict_free(dict);
- return false;
- }
-
+ object_to_vim_take_luaref(&item->value, &di->di_tv, take_luaref, err);
tv_dict_add(dict, di);
}
dict->dv_refcount++;
@@ -357,12 +344,16 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
}
case kObjectTypeLuaRef: {
- char *name = register_luafunc(api_new_luaref(obj.data.luaref));
+ LuaRef ref = obj->data.luaref;
+ if (take_luaref) {
+ obj->data.luaref = LUA_NOREF;
+ } else {
+ ref = api_new_luaref(ref);
+ }
+ char *name = register_luafunc(ref);
tv->v_type = VAR_FUNC;
tv->vval.v_string = xstrdup(name);
break;
}
}
-
- return true;
}