mirror of
https://github.com/neovim/neovim.git
synced 2025-10-08 02:46:31 +00:00
Merge pull request #19906 from bfredl/bigstage
perf(api): allow to use an arena for return values
This commit is contained in:
@@ -887,12 +887,12 @@ static bool check_autocmd_string_array(Array arr, char *k, Error *err)
|
||||
static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err)
|
||||
{
|
||||
if (v->type == kObjectTypeString) {
|
||||
ADD(*array, copy_object(*v));
|
||||
ADD(*array, copy_object(*v, NULL));
|
||||
} else if (v->type == kObjectTypeArray) {
|
||||
if (!check_autocmd_string_array(v->data.array, k, err)) {
|
||||
return false;
|
||||
}
|
||||
*array = copy_array(v->data.array);
|
||||
*array = copy_array(v->data.array, NULL);
|
||||
} else {
|
||||
if (required) {
|
||||
api_set_error(err,
|
||||
|
@@ -1021,7 +1021,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
|
||||
/// @param buffer Buffer handle, or 0 for current buffer
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Buffer name
|
||||
String nvim_buf_get_name(Buffer buffer, Error *err)
|
||||
String nvim_buf_get_name(Buffer buffer, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
String rv = STRING_INIT;
|
||||
@@ -1031,7 +1031,7 @@ String nvim_buf_get_name(Buffer buffer, Error *err)
|
||||
return rv;
|
||||
}
|
||||
|
||||
return cstr_to_string((char *)buf->b_ffname);
|
||||
return cstr_as_string((char *)buf->b_ffname);
|
||||
}
|
||||
|
||||
/// Sets the full file name for a buffer
|
||||
|
@@ -51,7 +51,7 @@ Integer nvim_create_namespace(String name)
|
||||
}
|
||||
id = next_namespace_id++;
|
||||
if (name.size > 0) {
|
||||
String name_alloc = copy_string(name);
|
||||
String name_alloc = copy_string(name, NULL);
|
||||
map_put(String, handle_T)(&namespace_ids, name_alloc, id);
|
||||
}
|
||||
return (Integer)id;
|
||||
|
@@ -5,18 +5,23 @@
|
||||
|
||||
typedef Object (*ApiDispatchWrapper)(uint64_t channel_id,
|
||||
Array args,
|
||||
Arena *arena,
|
||||
Error *error);
|
||||
|
||||
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
|
||||
/// functions of this type.
|
||||
typedef struct {
|
||||
struct MsgpackRpcRequestHandler {
|
||||
const char *name;
|
||||
ApiDispatchWrapper fn;
|
||||
bool fast; // Function is safe to be executed immediately while running the
|
||||
// uv loop (the loop is run very frequently due to breakcheck).
|
||||
// If "fast" is false, the function is deferred, i e the call will
|
||||
// be put in the event queue, for safe handling later.
|
||||
} MsgpackRpcRequestHandler;
|
||||
bool arena_return; // return value is allocated in the arena (or statically)
|
||||
// and should not be freed as such.
|
||||
};
|
||||
|
||||
extern const MsgpackRpcRequestHandler method_handlers[];
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "api/private/dispatch.h.generated.h"
|
||||
|
@@ -618,6 +618,7 @@ void api_clear_error(Error *value)
|
||||
value->type = kErrorTypeNone;
|
||||
}
|
||||
|
||||
/// @returns a shared value. caller must not modify it!
|
||||
Dictionary api_metadata(void)
|
||||
{
|
||||
static Dictionary metadata = ARRAY_DICT_INIT;
|
||||
@@ -630,7 +631,7 @@ Dictionary api_metadata(void)
|
||||
init_type_metadata(&metadata);
|
||||
}
|
||||
|
||||
return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary;
|
||||
return metadata;
|
||||
}
|
||||
|
||||
static void init_function_metadata(Dictionary *metadata)
|
||||
@@ -715,36 +716,40 @@ static void init_type_metadata(Dictionary *metadata)
|
||||
PUT(*metadata, "types", DICTIONARY_OBJ(types));
|
||||
}
|
||||
|
||||
String copy_string(String str)
|
||||
// all the copy_[object] functions allow arena=NULL,
|
||||
// then global allocations are used, and the resulting object
|
||||
// should be freed with an api_free_[object] function
|
||||
|
||||
String copy_string(String str, Arena *arena)
|
||||
{
|
||||
if (str.data != NULL) {
|
||||
return (String){ .data = xmemdupz(str.data, str.size), .size = str.size };
|
||||
return (String){ .data = arena_memdupz(arena, str.data, str.size), .size = str.size };
|
||||
} else {
|
||||
return (String)STRING_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
Array copy_array(Array array)
|
||||
Array copy_array(Array array, Arena *arena)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
Array rv = arena_array(arena, array.size);
|
||||
for (size_t i = 0; i < array.size; i++) {
|
||||
ADD(rv, copy_object(array.items[i]));
|
||||
ADD(rv, copy_object(array.items[i], arena));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Dictionary copy_dictionary(Dictionary dict)
|
||||
Dictionary copy_dictionary(Dictionary dict, Arena *arena)
|
||||
{
|
||||
Dictionary rv = ARRAY_DICT_INIT;
|
||||
Dictionary rv = arena_dict(arena, dict.size);
|
||||
for (size_t i = 0; i < dict.size; i++) {
|
||||
KeyValuePair item = dict.items[i];
|
||||
PUT(rv, item.key.data, copy_object(item.value));
|
||||
PUT_C(rv, copy_string(item.key, arena).data, copy_object(item.value, arena));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/// Creates a deep clone of an object
|
||||
Object copy_object(Object obj)
|
||||
Object copy_object(Object obj, Arena *arena)
|
||||
{
|
||||
switch (obj.type) {
|
||||
case kObjectTypeBuffer:
|
||||
@@ -757,13 +762,13 @@ Object copy_object(Object obj)
|
||||
return obj;
|
||||
|
||||
case kObjectTypeString:
|
||||
return STRING_OBJ(copy_string(obj.data.string));
|
||||
return STRING_OBJ(copy_string(obj.data.string, arena));
|
||||
|
||||
case kObjectTypeArray:
|
||||
return ARRAY_OBJ(copy_array(obj.data.array));
|
||||
return ARRAY_OBJ(copy_array(obj.data.array, arena));
|
||||
|
||||
case kObjectTypeDictionary:
|
||||
return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary));
|
||||
return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary, arena));
|
||||
|
||||
case kObjectTypeLuaRef:
|
||||
return LUAREF_OBJ(api_new_luaref(obj.data.luaref));
|
||||
@@ -844,7 +849,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
String str = copy_string(chunk.items[0].data.string);
|
||||
String str = copy_string(chunk.items[0].data.string, NULL);
|
||||
|
||||
int attr = 0;
|
||||
if (chunk.size == 2) {
|
||||
|
@@ -965,7 +965,7 @@ static Array translate_contents(UI *ui, Array contents)
|
||||
} else {
|
||||
ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
|
||||
}
|
||||
ADD(new_item, copy_object(item.items[1]));
|
||||
ADD(new_item, copy_object(item.items[1], NULL));
|
||||
ADD(new_contents, ARRAY_OBJ(new_item));
|
||||
}
|
||||
return new_contents;
|
||||
@@ -978,7 +978,7 @@ static Array translate_firstarg(UI *ui, Array args)
|
||||
|
||||
ADD(new_args, ARRAY_OBJ(translate_contents(ui, contents)));
|
||||
for (size_t i = 1; i < args.size; i++) {
|
||||
ADD(new_args, copy_object(args.items[i]));
|
||||
ADD(new_args, copy_object(args.items[i], NULL));
|
||||
}
|
||||
return new_args;
|
||||
}
|
||||
@@ -1024,7 +1024,7 @@ static void remote_ui_event(UI *ui, char *name, Array args)
|
||||
Array items = args.items[0].data.array;
|
||||
Array new_items = ARRAY_DICT_INIT;
|
||||
for (size_t i = 0; i < items.size; i++) {
|
||||
ADD(new_items, copy_object(items.items[i].data.array.items[0]));
|
||||
ADD(new_items, copy_object(items.items[i].data.array.items[0], NULL));
|
||||
}
|
||||
ADD_C(new_args, ARRAY_OBJ(new_items));
|
||||
push_call(ui, "wildmenu_show", new_args);
|
||||
|
@@ -1479,14 +1479,14 @@ void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err)
|
||||
/// 1 is the |api-metadata| map (Dictionary).
|
||||
///
|
||||
/// @returns 2-tuple [{channel-id}, {api-metadata}]
|
||||
Array nvim_get_api_info(uint64_t channel_id)
|
||||
Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
|
||||
FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_REMOTE_ONLY
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
Array rv = arena_array(arena, 2);
|
||||
|
||||
assert(channel_id <= INT64_MAX);
|
||||
ADD(rv, INTEGER_OBJ((int64_t)channel_id));
|
||||
ADD(rv, DICTIONARY_OBJ(api_metadata()));
|
||||
ADD_C(rv, INTEGER_OBJ((int64_t)channel_id));
|
||||
ADD_C(rv, DICTIONARY_OBJ(api_metadata()));
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -1545,9 +1545,9 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version,
|
||||
FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY
|
||||
{
|
||||
Dictionary info = ARRAY_DICT_INIT;
|
||||
PUT(info, "name", copy_object(STRING_OBJ(name)));
|
||||
PUT(info, "name", copy_object(STRING_OBJ(name), NULL));
|
||||
|
||||
version = copy_dictionary(version);
|
||||
version = copy_dictionary(version, NULL);
|
||||
bool has_major = false;
|
||||
for (size_t i = 0; i < version.size; i++) {
|
||||
if (strequal(version.items[i].key.data, "major")) {
|
||||
@@ -1560,9 +1560,9 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version,
|
||||
}
|
||||
PUT(info, "version", DICTIONARY_OBJ(version));
|
||||
|
||||
PUT(info, "type", copy_object(STRING_OBJ(type)));
|
||||
PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods)));
|
||||
PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes)));
|
||||
PUT(info, "type", copy_object(STRING_OBJ(type), NULL));
|
||||
PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods, NULL)));
|
||||
PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes, NULL)));
|
||||
|
||||
rpc_set_client_info(channel_id, info);
|
||||
}
|
||||
@@ -1629,11 +1629,11 @@ Array nvim_list_chans(void)
|
||||
/// an error, it is a three-element array with the zero-based index of the call
|
||||
/// which resulted in an error, the error type and the error message. If an
|
||||
/// error occurred, the values from all preceding calls will still be returned.
|
||||
Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
|
||||
Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
Array results = ARRAY_DICT_INIT;
|
||||
Array rv = arena_array(arena, 2);
|
||||
Array results = arena_array(arena, calls.size);
|
||||
Error nested_error = ERROR_INIT;
|
||||
|
||||
size_t i; // also used for freeing the variables
|
||||
@@ -1642,21 +1642,21 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
|
||||
api_set_error(err,
|
||||
kErrorTypeValidation,
|
||||
"Items in calls array must be arrays");
|
||||
goto validation_error;
|
||||
goto theend;
|
||||
}
|
||||
Array call = calls.items[i].data.array;
|
||||
if (call.size != 2) {
|
||||
api_set_error(err,
|
||||
kErrorTypeValidation,
|
||||
"Items in calls array must be arrays of size 2");
|
||||
goto validation_error;
|
||||
goto theend;
|
||||
}
|
||||
|
||||
if (call.items[0].type != kObjectTypeString) {
|
||||
api_set_error(err,
|
||||
kErrorTypeValidation,
|
||||
"Name must be String");
|
||||
goto validation_error;
|
||||
goto theend;
|
||||
}
|
||||
String name = call.items[0].data.string;
|
||||
|
||||
@@ -1664,7 +1664,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
|
||||
api_set_error(err,
|
||||
kErrorTypeValidation,
|
||||
"Args must be Array");
|
||||
goto validation_error;
|
||||
goto theend;
|
||||
}
|
||||
Array args = call.items[1].data.array;
|
||||
|
||||
@@ -1676,29 +1676,32 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
|
||||
if (ERROR_SET(&nested_error)) {
|
||||
break;
|
||||
}
|
||||
Object result = handler.fn(channel_id, args, &nested_error);
|
||||
|
||||
Object result = handler.fn(channel_id, args, arena, &nested_error);
|
||||
if (ERROR_SET(&nested_error)) {
|
||||
// error handled after loop
|
||||
break;
|
||||
}
|
||||
|
||||
ADD(results, result);
|
||||
// TODO(bfredl): wastefull copy. It could be avoided to encoding to msgpack
|
||||
// directly here. But `result` might become invalid when next api function
|
||||
// is called in the loop.
|
||||
ADD_C(results, copy_object(result, arena));
|
||||
if (!handler.arena_return) {
|
||||
api_free_object(result);
|
||||
}
|
||||
}
|
||||
|
||||
ADD(rv, ARRAY_OBJ(results));
|
||||
ADD_C(rv, ARRAY_OBJ(results));
|
||||
if (ERROR_SET(&nested_error)) {
|
||||
Array errval = ARRAY_DICT_INIT;
|
||||
ADD(errval, INTEGER_OBJ((Integer)i));
|
||||
ADD(errval, INTEGER_OBJ(nested_error.type));
|
||||
ADD(errval, STRING_OBJ(cstr_to_string(nested_error.msg)));
|
||||
ADD(rv, ARRAY_OBJ(errval));
|
||||
Array errval = arena_array(arena, 3);
|
||||
ADD_C(errval, INTEGER_OBJ((Integer)i));
|
||||
ADD_C(errval, INTEGER_OBJ(nested_error.type));
|
||||
ADD_C(errval, STRING_OBJ(copy_string(cstr_as_string(nested_error.msg), arena)));
|
||||
ADD_C(rv, ARRAY_OBJ(errval));
|
||||
} else {
|
||||
ADD(rv, NIL);
|
||||
ADD_C(rv, NIL);
|
||||
}
|
||||
goto theend;
|
||||
|
||||
validation_error:
|
||||
api_free_array(results);
|
||||
theend:
|
||||
api_clear_error(&nested_error);
|
||||
return rv;
|
||||
@@ -1751,7 +1754,7 @@ static void write_msg(String message, bool to_err)
|
||||
/// @return its argument.
|
||||
Object nvim__id(Object obj)
|
||||
{
|
||||
return copy_object(obj);
|
||||
return copy_object(obj, NULL);
|
||||
}
|
||||
|
||||
/// Returns array given as argument.
|
||||
@@ -1764,7 +1767,7 @@ Object nvim__id(Object obj)
|
||||
/// @return its argument.
|
||||
Array nvim__id_array(Array arr)
|
||||
{
|
||||
return copy_object(ARRAY_OBJ(arr)).data.array;
|
||||
return copy_array(arr, NULL);
|
||||
}
|
||||
|
||||
/// Returns dictionary given as argument.
|
||||
@@ -1777,7 +1780,7 @@ Array nvim__id_array(Array arr)
|
||||
/// @return its argument.
|
||||
Dictionary nvim__id_dictionary(Dictionary dct)
|
||||
{
|
||||
return copy_object(DICTIONARY_OBJ(dct)).data.dictionary;
|
||||
return copy_dictionary(dct, NULL);
|
||||
}
|
||||
|
||||
/// Returns floating-point value given as argument.
|
||||
@@ -1803,6 +1806,7 @@ Dictionary nvim__stats(void)
|
||||
PUT(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip));
|
||||
PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count()));
|
||||
PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw));
|
||||
PUT(rv, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count));
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user