refactor(typval): don't use msgpack_packer for msgpackdump()

Step towords completely eliminating msgpack_packer.
This commit is contained in:
bfredl 2024-06-15 19:50:44 +02:00
parent da4e8dc5b0
commit 2bb1a18631
9 changed files with 146 additions and 109 deletions

View File

@ -28,6 +28,7 @@ typedef struct {
#endif
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
#define TYPVAL_ENCODE_CHECK_BEFORE
#define TYPVAL_ENCODE_CONV_NIL(tv) \
kvi_push(edata->stack, NIL)
@ -217,6 +218,7 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
#undef TYPVAL_ENCODE_CONV_LIST_START
#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CHECK_BEFORE
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER

View File

@ -13,7 +13,7 @@
#include <string.h>
#include "klib/kvec.h"
#include "msgpack/pack.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
@ -28,6 +28,7 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/msgpack_rpc/packer.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h" // For _()
@ -412,6 +413,8 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
ga_concat(gap, "{}")
#define TYPVAL_ENCODE_CHECK_BEFORE
#define TYPVAL_ENCODE_CONV_NIL(tv) \
ga_concat(gap, "v:null")
@ -536,6 +539,8 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
#define TYPVAL_ENCODE_CHECK_BEFORE
#undef TYPVAL_ENCODE_CONV_NIL
#define TYPVAL_ENCODE_CONV_NIL(tv) \
ga_concat(gap, "null")
@ -821,6 +826,7 @@ bool encode_check_json_key(const typval_T *const tv)
#undef TYPVAL_ENCODE_CONV_LIST_START
#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CHECK_BEFORE
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
@ -913,52 +919,22 @@ char *encode_tv2json(typval_T *tv, size_t *len)
}
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
if ((buf) == NULL) { \
msgpack_pack_bin(packer, 0); \
} else { \
const size_t len_ = (len); \
msgpack_pack_bin(packer, len_); \
msgpack_pack_bin_body(packer, buf, len_); \
} \
} while (0)
mpack_bin(buf, (len), packer); \
#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
do { \
if ((buf) == NULL) { \
msgpack_pack_str(packer, 0); \
} else { \
const size_t len_ = (len); \
msgpack_pack_str(packer, len_); \
msgpack_pack_str_body(packer, buf, len_); \
} \
} while (0)
mpack_str(cbuf_as_string(buf, (len)), packer); \
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
do { \
if ((buf) == NULL) { \
msgpack_pack_ext(packer, 0, (int8_t)(type)); \
} else { \
const size_t len_ = (len); \
msgpack_pack_ext(packer, len_, (int8_t)(type)); \
msgpack_pack_ext_body(packer, buf, len_); \
} \
} while (0)
mpack_ext(buf, (len), (int8_t)(type), packer); \
#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
do { \
const size_t len_ = (size_t)(len); \
msgpack_pack_bin(packer, len_); \
if (len_ > 0) { \
msgpack_pack_bin_body(packer, (blob)->bv_ga.ga_data, len_); \
} \
} while (0)
mpack_bin((blob) ? (blob)->bv_ga.ga_data : NULL, (size_t)(len), packer); \
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
msgpack_pack_int64(packer, (int64_t)(num))
mpack_integer(&packer->ptr, (Integer)(num))
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
msgpack_pack_double(packer, (double)(flt))
mpack_float8(&packer->ptr, (double)(flt))
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
return conv_error(_("E5004: Error while dumping %s, %s: " \
@ -970,33 +946,30 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
msgpack_pack_array(packer, 0)
mpack_array(&packer->ptr, 0)
#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
msgpack_pack_array(packer, (size_t)(len))
mpack_array(&packer->ptr, (uint32_t)(len))
#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
msgpack_pack_map(packer, 0)
mpack_map(&packer->ptr, 0)
#define TYPVAL_ENCODE_CHECK_BEFORE \
mpack_check_buffer(packer)
#define TYPVAL_ENCODE_CONV_NIL(tv) \
msgpack_pack_nil(packer)
mpack_nil(&packer->ptr)
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
do { \
if (num) { \
msgpack_pack_true(packer); \
} else { \
msgpack_pack_false(packer); \
} \
} while (0)
mpack_bool(&packer->ptr, (bool)num); \
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
msgpack_pack_uint64(packer, (num))
mpack_uint64(&packer->ptr, (num))
#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
msgpack_pack_map(packer, (size_t)(len))
mpack_map(&packer->ptr, (uint32_t)(len))
#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
@ -1021,7 +994,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_SCOPE
#define TYPVAL_ENCODE_NAME msgpack
#define TYPVAL_ENCODE_FIRST_ARG_TYPE msgpack_packer *const
#define TYPVAL_ENCODE_FIRST_ARG_TYPE PackerBuffer *const
#define TYPVAL_ENCODE_FIRST_ARG_NAME packer
#include "nvim/eval/typval_encode.c.h"
#undef TYPVAL_ENCODE_SCOPE
@ -1043,6 +1016,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#undef TYPVAL_ENCODE_CONV_LIST_START
#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CHECK_BEFORE
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER

View File

@ -5,6 +5,7 @@
#include "nvim/eval/typval_defs.h"
#include "nvim/garray_defs.h"
#include "nvim/msgpack_rpc/packer_defs.h"
/// Convert Vimscript value to msgpack string
///
@ -13,7 +14,7 @@
/// @param[in] objname Object name, used for error message.
///
/// @return OK in case of success, FAIL otherwise.
int encode_vim_to_msgpack(msgpack_packer *packer, typval_T *tv, const char *objname);
int encode_vim_to_msgpack(PackerBuffer *packer, typval_T *tv, const char *objname);
/// Convert Vimscript value to :echo output
///

View File

@ -94,6 +94,7 @@
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/packer.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/normal.h"
#include "nvim/normal_defs.h"
@ -5501,15 +5502,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
list_T *const list = argvars[0].vval.v_list;
msgpack_packer *packer;
if (argvars[1].v_type != VAR_UNKNOWN
&& strequal(tv_get_string(&argvars[1]), "B")) {
tv_blob_alloc_ret(rettv);
packer = msgpack_packer_new(rettv->vval.v_blob, &encode_blob_write);
} else {
packer = msgpack_packer_new(tv_list_alloc_ret(rettv, kListLenMayKnow),
&encode_list_write);
}
PackerBuffer packer = packer_string_buffer();
const char *const msg = _("msgpackdump() argument, index %i");
// Assume that translation will not take more then 4 times more space
char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN];
@ -5517,11 +5510,20 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
TV_LIST_ITER(list, li, {
vim_snprintf(msgbuf, sizeof(msgbuf), msg, idx);
idx++;
if (encode_vim_to_msgpack(packer, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) {
if (encode_vim_to_msgpack(&packer, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) {
break;
}
});
msgpack_packer_free(packer);
String data = packer_take_string(&packer);
if (argvars[1].v_type != VAR_UNKNOWN && strequal(tv_get_string(&argvars[1]), "B")) {
blob_T *b = tv_blob_alloc_ret(rettv);
b->bv_ga.ga_data = data.data;
b->bv_ga.ga_len = (int)data.size;
b->bv_ga.ga_maxlen = (int)(packer.endptr - packer.startptr);
} else {
encode_list_write(tv_list_alloc_ret(rettv, kListLenMayKnow), data.data, data.size);
api_free_string(data);
}
}
static int msgpackparse_convert_item(const msgpack_object data, const msgpack_unpack_return result,

View File

@ -3064,8 +3064,7 @@ void f_blob2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// list2blob() function
void f_list2blob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_blob_alloc_ret(rettv);
blob_T *const blob = rettv->vval.v_blob;
blob_T *blob = tv_blob_alloc_ret(rettv);
if (tv_check_for_list_arg(argvars, 0) == FAIL) {
return;
@ -3252,11 +3251,12 @@ void tv_dict_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
/// Also sets reference count.
///
/// @param[out] ret_tv Structure where blob is saved.
void tv_blob_alloc_ret(typval_T *const ret_tv)
blob_T *tv_blob_alloc_ret(typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL
{
blob_T *const b = tv_blob_alloc();
tv_blob_set_ret(ret_tv, b);
return b;
}
/// Copy a blob typval to a different typval.
@ -3284,6 +3284,7 @@ void tv_blob_copy(blob_T *const from, typval_T *const to)
//{{{3 Clear
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
#define TYPVAL_ENCODE_CHECK_BEFORE
#define TYPVAL_ENCODE_CONV_NIL(tv) \
do { \
@ -3500,6 +3501,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, dict_T **const dic
#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
#undef TYPVAL_ENCODE_CHECK_BEFORE
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_NUMBER

View File

@ -6,6 +6,11 @@
/// something else. For these macros to work the following macros must be
/// defined:
/// @def TYPVAL_ENCODE_CHECK_BEFORE
/// @brief Macro used before any specific CONV function
///
/// can be used for a common check, like flushing a buffer if necessary
/// @def TYPVAL_ENCODE_CONV_NIL
/// @brief Macros used to convert NIL value
///
@ -323,6 +328,7 @@ static int TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, MPConvStack *const mpstack,
MPConvStackVal *const cur_mpsv, typval_T *const tv, const int copyID, const char *const objname)
{
TYPVAL_ENCODE_CHECK_BEFORE;
switch (tv->v_type) {
case VAR_STRING:
TYPVAL_ENCODE_CONV_STRING(tv, tv->vval.v_string, tv_strlen(tv));
@ -420,6 +426,7 @@ static int TYPVAL_ENCODE_CONVERT_ONE_VALUE(
break;
}
}
TYPVAL_ENCODE_CHECK_BEFORE;
if (i == ARRAY_SIZE(eval_msgpack_type_lists)) {
goto _convert_one_value_regular_dict;
}

View File

@ -425,6 +425,8 @@ static bool typval_conv_special = false;
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
#define TYPVAL_ENCODE_CHECK_BEFORE
#define TYPVAL_ENCODE_CONV_NIL(tv) \
do { \
if (typval_conv_special) { \
@ -574,6 +576,7 @@ static bool typval_conv_special = false;
#undef TYPVAL_ENCODE_CONV_LIST_START
#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CHECK_BEFORE
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER

View File

@ -8,7 +8,7 @@
# include "msgpack_rpc/packer.c.generated.h"
#endif
static void check_buffer(PackerBuffer *packer)
void mpack_check_buffer(PackerBuffer *packer)
{
if (mpack_remaining(packer) < MPACK_ITEM_SIZE) {
packer->packer_flush(packer);
@ -87,7 +87,25 @@ void mpack_str(String str, PackerBuffer *packer)
mpack_raw(str.data, len, packer);
}
void mpack_raw(char *data, size_t len, PackerBuffer *packer)
void mpack_bin(const char *data, size_t len, PackerBuffer *packer)
{
if (len < 0xff) {
mpack_w(&packer->ptr, 0xc4);
mpack_w(&packer->ptr, len);
} else if (len < 0xffff) {
mpack_w(&packer->ptr, 0xc5);
mpack_w2(&packer->ptr, (uint32_t)len);
} else if (len < 0xffffffff) {
mpack_w(&packer->ptr, 0xc6);
mpack_w4(&packer->ptr, (uint32_t)len);
} else {
abort();
}
mpack_raw(data, len, packer);
}
void mpack_raw(const char *data, size_t len, PackerBuffer *packer)
{
size_t pos = 0;
while (pos < len) {
@ -103,6 +121,27 @@ void mpack_raw(char *data, size_t len, PackerBuffer *packer)
}
}
void mpack_ext(char *buf, size_t len, int8_t type, PackerBuffer *packer)
{
if (len == 1) {
mpack_w(&packer->ptr, 0xd4);
} else if (len == 2) {
mpack_w(&packer->ptr, 0xd5);
} else if (len <= 0xff) {
mpack_w(&packer->ptr, 0xc7);
} else if (len < 0xffff) {
mpack_w(&packer->ptr, 0xc8);
mpack_w2(&packer->ptr, (uint32_t)len);
} else if (len < 0xffffffff) {
mpack_w(&packer->ptr, 0xc9);
mpack_w4(&packer->ptr, (uint32_t)len);
} else {
abort();
}
mpack_w(&packer->ptr, type);
mpack_raw(buf, len, packer);
}
void mpack_handle(ObjectType type, handle_T handle, PackerBuffer *packer)
{
char exttype = (char)(type - EXT_OBJECT_TYPE_SHIFT);
@ -156,7 +195,7 @@ void mpack_object_inner(Object *current, Object *container, size_t container_idx
kvi_init(stack);
while (true) {
check_buffer(packer);
mpack_check_buffer(packer);
switch (current->type) {
case kObjectTypeLuaRef:
// TODO(bfredl): could also be an error. Though kObjectTypeLuaRef
@ -231,7 +270,7 @@ void mpack_object_inner(Object *current, Object *container, size_t container_idx
} else {
Dictionary dict = container->data.dictionary;
KeyValuePair *it = &dict.items[container_idx++];
check_buffer(packer);
mpack_check_buffer(packer);
mpack_str(it->key, packer);
current = &it->value;
if (container_idx >= dict.size) {
@ -241,3 +280,32 @@ void mpack_object_inner(Object *current, Object *container, size_t container_idx
}
kvi_destroy(stack);
}
PackerBuffer packer_string_buffer(void)
{
const size_t initial_size = 64; // must be larger than SHADA_MPACK_FREE_SPACE
char *alloc = xmalloc(initial_size);
return (PackerBuffer) {
.startptr = alloc,
.ptr = alloc,
.endptr = alloc + initial_size,
.packer_flush = flush_string_buffer,
};
}
static void flush_string_buffer(PackerBuffer *buffer)
{
size_t current_capacity = (size_t)(buffer->endptr - buffer->startptr);
size_t new_capacity = 2 * current_capacity;
size_t len = (size_t)(buffer->ptr - buffer->startptr);
buffer->startptr = xrealloc(buffer->startptr, new_capacity);
buffer->ptr = buffer->startptr + len;
buffer->endptr = buffer->startptr + new_capacity;
}
/// can only be used with a PackerBuffer from `packer_string_buffer`
String packer_take_string(PackerBuffer *buffer)
{
return (String){ .data = buffer->startptr, .size = (size_t)(buffer->ptr - buffer->startptr) };
}

View File

@ -1354,6 +1354,12 @@ static char *shada_filename(const char *file)
#define SHADA_MPACK_FREE_SPACE (4 * MPACK_ITEM_SIZE)
static int mpack_raw_wrapper(void *cookie, const char *data, size_t len)
{
mpack_raw(data, len, (PackerBuffer *)cookie);
return 0;
}
/// Write single ShaDa entry
///
/// @param[in] packer Packer used to write entry.
@ -1367,14 +1373,13 @@ static ShaDaWriteResult shada_pack_entry(PackerBuffer *const packer, ShadaEntry
FUNC_ATTR_NONNULL_ALL
{
ShaDaWriteResult ret = kSDWriteFailed;
msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf);
msgpack_packer *spacker = msgpack_packer_new(&sbuf, &msgpack_sbuffer_write);
PackerBuffer sbuf = packer_string_buffer();
msgpack_packer *spacker = msgpack_packer_new(&sbuf, &mpack_raw_wrapper);
#define DUMP_ADDITIONAL_ELEMENTS(src, what) \
do { \
if ((src) != NULL) { \
TV_LIST_ITER((src), li, { \
if (encode_vim_to_msgpack(spacker, TV_LIST_ITEM_TV(li), \
if (encode_vim_to_msgpack(&sbuf, TV_LIST_ITEM_TV(li), \
_("additional elements of ShaDa " what)) \
== FAIL) { \
goto shada_pack_entry_error; \
@ -1394,7 +1399,7 @@ static ShaDaWriteResult shada_pack_entry(PackerBuffer *const packer, ShadaEntry
const size_t key_len = strlen(hi->hi_key); \
msgpack_pack_str(spacker, key_len); \
msgpack_pack_str_body(spacker, hi->hi_key, key_len); \
if (encode_vim_to_msgpack(spacker, &di->di_tv, \
if (encode_vim_to_msgpack(&sbuf, &di->di_tv, \
_("additional data of ShaDa " what)) \
== FAIL) { \
goto shada_pack_entry_error; \
@ -1460,7 +1465,7 @@ static ShaDaWriteResult shada_pack_entry(PackerBuffer *const packer, ShadaEntry
char vardesc[256] = "variable g:";
memcpy(&vardesc[sizeof("variable g:") - 1], varname.data,
varname.size + 1);
if (encode_vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc)
if (encode_vim_to_msgpack(&sbuf, &entry.data.global_var.value, vardesc)
== FAIL) {
ret = kSDWriteIgnError;
semsg(_(WERR "Failed to write variable %s"),
@ -1645,7 +1650,8 @@ static ShaDaWriteResult shada_pack_entry(PackerBuffer *const packer, ShadaEntry
}
#undef CHECK_DEFAULT
#undef ONE_IF_NOT_DEFAULT
if (!max_kbyte || sbuf.size <= max_kbyte * 1024) {
String packed = packer_take_string(&sbuf);
if (!max_kbyte || packed.size <= max_kbyte * 1024) {
if (mpack_remaining(packer) < SHADA_MPACK_FREE_SPACE) {
packer->packer_flush(packer);
}
@ -1656,9 +1662,9 @@ static ShaDaWriteResult shada_pack_entry(PackerBuffer *const packer, ShadaEntry
mpack_uint64(&packer->ptr, (uint64_t)entry.type);
}
mpack_uint64(&packer->ptr, (uint64_t)entry.timestamp);
if (sbuf.size > 0) {
mpack_uint64(&packer->ptr, (uint64_t)sbuf.size);
mpack_raw(sbuf.data, sbuf.size, packer);
if (packed.size > 0) {
mpack_uint64(&packer->ptr, (uint64_t)packed.size);
mpack_raw(packed.data, packed.size, packer);
}
if (packer->anyint != 0) { // error code
@ -1668,7 +1674,7 @@ static ShaDaWriteResult shada_pack_entry(PackerBuffer *const packer, ShadaEntry
ret = kSDWriteSuccessful;
shada_pack_entry_error:
msgpack_packer_free(spacker);
msgpack_sbuffer_destroy(&sbuf);
xfree(sbuf.startptr);
return ret;
}
@ -3960,34 +3966,6 @@ static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps,
return jumps_size;
}
static PackerBuffer packer_string_buffer(void)
{
const size_t initial_size = 64; // must be larger than SHADA_MPACK_FREE_SPACE
char *alloc = xmalloc(initial_size);
return (PackerBuffer) {
.startptr = alloc,
.ptr = alloc,
.endptr = alloc + initial_size,
.packer_flush = flush_string_buffer,
};
}
static void flush_string_buffer(PackerBuffer *buffer)
{
size_t current_capacity = (size_t)(buffer->endptr - buffer->startptr);
size_t new_capacity = 2 * current_capacity;
size_t len = (size_t)(buffer->ptr - buffer->startptr);
buffer->startptr = xrealloc(buffer->startptr, new_capacity);
buffer->ptr = buffer->startptr + len;
buffer->endptr = buffer->startptr + new_capacity;
}
static String packer_take_string(PackerBuffer *buffer)
{
return (String){ .data = buffer->startptr, .size = (size_t)(buffer->ptr - buffer->startptr) };
}
/// Write registers ShaDa entries in given msgpack_sbuffer.
///
/// @param[in] sbuf target msgpack_sbuffer to write to.