diff options
Diffstat (limited to 'src/nvim/shada.c')
-rw-r--r-- | src/nvim/shada.c | 1706 |
1 files changed, 602 insertions, 1104 deletions
diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 34c36f850f..2b401fa106 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1,9 +1,5 @@ #include <assert.h> #include <inttypes.h> -#include <msgpack/object.h> -#include <msgpack/pack.h> -#include <msgpack/sbuffer.h> -#include <msgpack/unpack.h> #include <stdbool.h> #include <stddef.h> #include <stdio.h> @@ -13,12 +9,14 @@ #include <uv.h> #include "auto/config.h" +#include "nvim/api/keysets_defs.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/cmdhist.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/decode.h" #include "nvim/eval/encode.h" @@ -41,6 +39,8 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/msgpack_rpc/packer.h" +#include "nvim/msgpack_rpc/unpacker.h" #include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" @@ -69,26 +69,26 @@ # include ENDIAN_INCLUDE_FILE #endif -#define SEARCH_KEY_MAGIC "sm" -#define SEARCH_KEY_SMARTCASE "sc" -#define SEARCH_KEY_HAS_LINE_OFFSET "sl" -#define SEARCH_KEY_PLACE_CURSOR_AT_END "se" -#define SEARCH_KEY_IS_LAST_USED "su" -#define SEARCH_KEY_IS_SUBSTITUTE_PATTERN "ss" -#define SEARCH_KEY_HIGHLIGHTED "sh" -#define SEARCH_KEY_OFFSET "so" -#define SEARCH_KEY_PAT "sp" -#define SEARCH_KEY_BACKWARD "sb" - -#define REG_KEY_TYPE "rt" -#define REG_KEY_WIDTH "rw" -#define REG_KEY_CONTENTS "rc" -#define REG_KEY_UNNAMED "ru" - -#define KEY_LNUM "l" -#define KEY_COL "c" -#define KEY_FILE "f" -#define KEY_NAME_CHAR "n" +#define SEARCH_KEY_MAGIC sm +#define SEARCH_KEY_SMARTCASE sc +#define SEARCH_KEY_HAS_LINE_OFFSET sl +#define SEARCH_KEY_PLACE_CURSOR_AT_END se +#define SEARCH_KEY_IS_LAST_USED su +#define SEARCH_KEY_IS_SUBSTITUTE_PATTERN ss +#define SEARCH_KEY_HIGHLIGHTED sh +#define SEARCH_KEY_OFFSET so +#define SEARCH_KEY_PAT sp +#define SEARCH_KEY_BACKWARD sb + +#define REG_KEY_TYPE rt +#define REG_KEY_WIDTH rw +#define REG_KEY_CONTENTS rc +#define REG_KEY_UNNAMED ru + +#define KEY_LNUM l +#define KEY_COL c +#define KEY_FILE f +#define KEY_NAME_CHAR n // Error messages formerly used by viminfo code: // E136: viminfo: Too many errors, skipping rest of file @@ -231,31 +231,17 @@ typedef struct { ShadaEntryType type; Timestamp timestamp; union { - Dictionary header; + Dict header; struct shada_filemark { char name; pos_T mark; char *fname; - dict_T *additional_data; } filemark; - struct search_pattern { - bool magic; - bool smartcase; - bool has_line_offset; - bool place_cursor_at_end; - int64_t offset; - bool is_last_used; - bool is_substitute_pattern; - bool highlighted; - bool search_backward; - char *pat; - dict_T *additional_data; - } search_pattern; + Dict(_shada_search_pat) search_pattern; struct history_item { uint8_t histtype; char *string; char sep; - list_T *additional_elements; } history_item; struct reg { // yankreg_T char name; @@ -264,12 +250,10 @@ typedef struct { bool is_unnamed; size_t contents_size; size_t width; - dict_T *additional_data; } reg; struct global_var { char *name; typval_T value; - list_T *additional_elements; } global_var; struct { uint64_t type; @@ -278,17 +262,17 @@ typedef struct { } unknown_item; struct sub_string { char *sub; - list_T *additional_elements; } sub_string; struct buffer_list { size_t size; struct buffer_list_buffer { pos_T pos; char *fname; - dict_T *additional_data; + AdditionalData *additional_data; } *buffers; } buffer_list; } data; + AdditionalData *additional_data; } ShadaEntry; /// One entry in sized linked list @@ -356,35 +340,6 @@ typedef struct { PMap(cstr_t) file_marks; ///< All file marks. } WriteMergerState; -typedef struct sd_read_def ShaDaReadDef; - -/// Function used to close files defined by ShaDaReadDef -typedef void (*ShaDaReadCloser)(ShaDaReadDef *const sd_reader) - REAL_FATTR_NONNULL_ALL; - -/// Function used to read ShaDa files -typedef ptrdiff_t (*ShaDaFileReader)(ShaDaReadDef *const sd_reader, - void *const dest, - const size_t size) - REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; - -/// Function used to skip in ShaDa files -typedef int (*ShaDaFileSkipper)(ShaDaReadDef *const sd_reader, - const size_t offset) - REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; - -/// Structure containing necessary pointers for reading ShaDa files -struct sd_read_def { - ShaDaFileReader read; ///< Reader function. - ShaDaReadCloser close; ///< Close function. - ShaDaFileSkipper skip; ///< Function used to skip some bytes. - void *cookie; ///< Data describing object read from. - bool eof; ///< True if reader reached end of file. - const char *error; ///< Error message in case of error. - uintmax_t fpos; ///< Current position (amount of bytes read since - ///< reader structure initialization). May overflow. -}; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "shada.c.generated.h" #endif @@ -393,6 +348,7 @@ struct sd_read_def { [kSDItem##name] = { \ .timestamp = 0, \ .type = kSDItem##name, \ + .additional_data = NULL, \ .data = { \ .attr = { __VA_ARGS__ } \ } \ @@ -412,49 +368,41 @@ static const ShadaEntry sd_default_values[] = { .is_substitute_pattern = false, .highlighted = false, .search_backward = false, - .pat = NULL, - .additional_data = NULL), - DEF_SDE(SubString, sub_string, .sub = NULL, .additional_elements = NULL), + .pat = STRING_INIT), + DEF_SDE(SubString, sub_string, .sub = NULL), DEF_SDE(HistoryEntry, history_item, .histtype = HIST_CMD, .string = NULL, - .sep = NUL, - .additional_elements = NULL), + .sep = NUL), DEF_SDE(Register, reg, .name = NUL, .type = kMTCharWise, .contents = NULL, .contents_size = 0, .is_unnamed = false, - .width = 0, - .additional_data = NULL), + .width = 0), DEF_SDE(Variable, global_var, .name = NULL, - .value = { .v_type = VAR_UNKNOWN, .vval = { .v_string = NULL } }, - .additional_elements = NULL), + .value = { .v_type = VAR_UNKNOWN, .vval = { .v_string = NULL } }), DEF_SDE(GlobalMark, filemark, .name = '"', .mark = DEFAULT_POS, - .fname = NULL, - .additional_data = NULL), + .fname = NULL), DEF_SDE(Jump, filemark, .name = NUL, .mark = DEFAULT_POS, - .fname = NULL, - .additional_data = NULL), + .fname = NULL), DEF_SDE(BufferList, buffer_list, .size = 0, .buffers = NULL), DEF_SDE(LocalMark, filemark, .name = '"', .mark = DEFAULT_POS, - .fname = NULL, - .additional_data = NULL), + .fname = NULL), DEF_SDE(Change, filemark, .name = NUL, .mark = DEFAULT_POS, - .fname = NULL, - .additional_data = NULL), + .fname = NULL), }; #undef DEFAULT_POS #undef DEF_SDE @@ -587,70 +535,6 @@ static inline void hmll_dealloc(HMLList *const hmll) xfree(hmll->entries); } -/// Wrapper for reading from file descriptors -/// -/// @return -1 or number of bytes read. -static ptrdiff_t read_file(ShaDaReadDef *const sd_reader, void *const dest, const size_t size) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - const ptrdiff_t ret = file_read(sd_reader->cookie, dest, size); - sd_reader->eof = file_eof(sd_reader->cookie); - if (ret < 0) { - sd_reader->error = os_strerror((int)ret); - return -1; - } - sd_reader->fpos += (size_t)ret; - return ret; -} - -/// Read one character -static int read_char(ShaDaReadDef *const sd_reader) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - uint8_t ret; - ptrdiff_t read_bytes = sd_reader->read(sd_reader, &ret, 1); - if (read_bytes != 1) { - return EOF; - } - return (int)ret; -} - -/// Wrapper for closing file descriptors opened for reading -static void close_sd_reader(ShaDaReadDef *const sd_reader) - FUNC_ATTR_NONNULL_ALL -{ - close_file(sd_reader->cookie); - xfree(sd_reader->cookie); -} - -/// Wrapper for read that reads to IObuff and ignores bytes read -/// -/// Used for skipping. -/// -/// @param[in,out] sd_reader File read. -/// @param[in] offset Amount of bytes to skip. -/// -/// @return FAIL in case of failure, OK in case of success. May set -/// sd_reader->eof or sd_reader->error. -static int sd_reader_skip_read(ShaDaReadDef *const sd_reader, const size_t offset) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - const ptrdiff_t skip_bytes = file_skip(sd_reader->cookie, offset); - if (skip_bytes < 0) { - sd_reader->error = os_strerror((int)skip_bytes); - return FAIL; - } else if (skip_bytes != (ptrdiff_t)offset) { - assert(skip_bytes < (ptrdiff_t)offset); - sd_reader->eof = file_eof(sd_reader->cookie); - if (!sd_reader->eof) { - sd_reader->error = _("too few bytes read"); - } - return FAIL; - } - sd_reader->fpos += (size_t)skip_bytes; - return OK; -} - /// Wrapper for read that can be used when lseek cannot be used /// /// E.g. when trying to read from a pipe. @@ -660,55 +544,30 @@ static int sd_reader_skip_read(ShaDaReadDef *const sd_reader, const size_t offse /// /// @return kSDReadStatusReadError, kSDReadStatusNotShaDa or /// kSDReadStatusSuccess. -static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, const size_t offset) +static ShaDaReadResult sd_reader_skip(FileDescriptor *const sd_reader, const size_t offset) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - if (sd_reader->skip(sd_reader, offset) != OK) { - if (sd_reader->error != NULL) { - semsg(_(SERR "System error while skipping in ShaDa file: %s"), - sd_reader->error); - return kSDReadStatusReadError; - } else if (sd_reader->eof) { + const ptrdiff_t skip_bytes = file_skip(sd_reader, offset); + if (skip_bytes < 0) { + semsg(_(SERR "System error while skipping in ShaDa file: %s"), + os_strerror((int)skip_bytes)); + return kSDReadStatusReadError; + } else if (skip_bytes != (ptrdiff_t)offset) { + assert(skip_bytes < (ptrdiff_t)offset); + if (file_eof(sd_reader)) { semsg(_(RCERR "Error while reading ShaDa file: " "last entry specified that it occupies %" PRIu64 " bytes, " "but file ended earlier"), (uint64_t)offset); - return kSDReadStatusNotShaDa; + } else { + semsg(_(SERR "System error while skipping in ShaDa file: %s"), + _("too few bytes read")); } - abort(); + return kSDReadStatusNotShaDa; } return kSDReadStatusSuccess; } -/// Open ShaDa file for reading -/// -/// @param[in] fname File name to open. -/// @param[out] sd_reader Location where reader structure will be saved. -/// -/// @return libuv error in case of error, 0 otherwise. -static int open_shada_file_for_reading(const char *const fname, ShaDaReadDef *sd_reader) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - *sd_reader = (ShaDaReadDef) { - .read = &read_file, - .close = &close_sd_reader, - .skip = &sd_reader_skip_read, - .error = NULL, - .eof = false, - .fpos = 0, - .cookie = xmalloc(sizeof(FileDescriptor)), - }; - int error = file_open(sd_reader->cookie, fname, kFileReadOnly, 0); - if (error) { - XFREE_CLEAR(sd_reader->cookie); - return error; - } - - assert(strcmp(p_enc, "utf-8") == 0); - - return 0; -} - /// Wrapper for closing file descriptors static void close_file(FileDescriptor *cookie) { @@ -719,20 +578,6 @@ static void close_file(FileDescriptor *cookie) } } -/// Msgpack callback for writing to FileDescriptor* -static int msgpack_sd_writer_write(void *data, const char *buf, size_t len) -{ - FileDescriptor *const sd_writer = (FileDescriptor *)data; - const ptrdiff_t ret = file_write(sd_writer, buf, len); - if (ret < 0) { - semsg(_(SERR "System error while writing ShaDa file: %s"), - os_strerror((int)ret)); - return -1; - } - - return 0; -} - /// Check whether writing to shada file was disabled ("-i NONE" or "--clean"). /// /// @return true if it was disabled, false otherwise. @@ -757,8 +602,8 @@ static int shada_read_file(const char *const file, const int flags) char *const fname = shada_filename(file); - ShaDaReadDef sd_reader; - const int of_ret = open_shada_file_for_reading(fname, &sd_reader); + FileDescriptor sd_reader; + int of_ret = file_open(&sd_reader, fname, kFileReadOnly, 0); if (p_verbose > 1) { verbose_enter(); @@ -782,7 +627,7 @@ static int shada_read_file(const char *const file, const int flags) xfree(fname); shada_read(&sd_reader, flags); - sd_reader.close(&sd_reader); + close_file(&sd_reader); return OK; } @@ -815,9 +660,9 @@ static const void *shada_hist_iter(const void *const iter, const uint8_t history .sep = (char)(history_type == HIST_SEARCH ? hist_he.hisstr[strlen(hist_he.hisstr) + 1] : 0), - .additional_elements = hist_he.additional_elements, } - } + }, + .additional_data = hist_he.additional_data, }; } return ret; @@ -939,8 +784,7 @@ static inline void hms_to_he_array(const HistoryMergerState *const hms_p, hist->timestamp = cur_entry->data.timestamp; hist->hisnum = (int)(hist - hist_array) + 1; hist->hisstr = cur_entry->data.data.history_item.string; - hist->additional_elements = - cur_entry->data.data.history_item.additional_elements; + hist->additional_data = cur_entry->data.additional_data; hist++; }) *new_hisnum = (int)(hist - hist_array); @@ -968,7 +812,7 @@ static inline void hms_dealloc(HistoryMergerState *const hms_p) /// Iterate over global variables /// -/// @warning No modifications to global variable dictionary must be performed +/// @warning No modifications to global variable Dict must be performed /// while iteration is in progress. /// /// @param[in] iter Iterator. Pass NULL to start iteration. @@ -1094,7 +938,7 @@ static inline bool marks_equal(const pos_T a, const pos_T b) /// /// @param[in] sd_reader Structure containing file reader definition. /// @param[in] flags What to read, see ShaDaReadFileFlags enum. -static void shada_read(ShaDaReadDef *const sd_reader, const int flags) +static void shada_read(FileDescriptor *const sd_reader, const int flags) FUNC_ATTR_NONNULL_ALL { list_T *oldfiles_list = get_vim_var_list(VV_OLDFILES); @@ -1187,9 +1031,9 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) .end = cur_entry.data.search_pattern.place_cursor_at_end, .off = cur_entry.data.search_pattern.offset, }, - .pat = cur_entry.data.search_pattern.pat, - .patlen = strlen(cur_entry.data.search_pattern.pat), - .additional_data = cur_entry.data.search_pattern.additional_data, + .pat = cur_entry.data.search_pattern.pat.data, + .patlen = cur_entry.data.search_pattern.pat.size, + .additional_data = cur_entry.additional_data, .timestamp = cur_entry.timestamp, }; @@ -1217,7 +1061,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) sub_set_replacement((SubReplacementString) { .sub = cur_entry.data.sub_string.sub, .timestamp = cur_entry.timestamp, - .additional_elements = cur_entry.data.sub_string.additional_elements, + .additional_data = cur_entry.additional_data, }); // Without using regtilde and without / &cpo flag previous substitute // string is close to useless: you can only use it with :& or :~ and @@ -1255,7 +1099,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) .y_type = cur_entry.data.reg.type, .y_width = (colnr_T)cur_entry.data.reg.width, .timestamp = cur_entry.timestamp, - .additional_data = cur_entry.data.reg.additional_data, + .additional_data = cur_entry.additional_data, }, cur_entry.data.reg.is_unnamed)) { shada_free_shada_entry(&cur_entry); } @@ -1280,7 +1124,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) .fnum = (buf == NULL ? 0 : buf->b_fnum), .timestamp = cur_entry.timestamp, .view = INIT_FMARKV, - .additional_data = cur_entry.data.filemark.additional_data, + .additional_data = cur_entry.additional_data, }, }; if (cur_entry.type == kSDItemGlobalMark) { @@ -1322,8 +1166,9 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) cur_entry.data.buffer_list.buffers[i].pos, 0, view); buflist_setfpos(buf, curwin, buf->b_last_cursor.mark.lnum, buf->b_last_cursor.mark.col, false); - buf->additional_data = - cur_entry.data.buffer_list.buffers[i].additional_data; + + xfree(buf->additional_data); + buf->additional_data = cur_entry.data.buffer_list.buffers[i].additional_data; cur_entry.data.buffer_list.buffers[i].additional_data = NULL; } } @@ -1360,7 +1205,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) .fnum = 0, .timestamp = cur_entry.timestamp, .view = INIT_FMARKV, - .additional_data = cur_entry.data.filemark.additional_data, + .additional_data = cur_entry.additional_data, }; if (cur_entry.type == kSDItemLocalMark) { if (!mark_set_local(cur_entry.data.filemark.name, buf, fm, !force)) { @@ -1468,19 +1313,30 @@ static char *shada_filename(const char *file) return xstrdup(file); } -#define PACK_STATIC_STR(s) \ - do { \ - msgpack_pack_str(spacker, sizeof(s) - 1); \ - msgpack_pack_str_body(spacker, s, sizeof(s) - 1); \ - } while (0) -#define PACK_BIN(s) \ - do { \ - const String s_ = (s); \ - msgpack_pack_bin(spacker, s_.size); \ - if (s_.size > 0) { \ - msgpack_pack_bin_body(spacker, s_.data, s_.size); \ - } \ - } while (0) +#define KEY_NAME_(s) #s +#define PACK_KEY(s) mpack_str(STATIC_CSTR_AS_STRING(KEY_NAME_(s)), &sbuf); +#define KEY_NAME(s) KEY_NAME_(s) + +#define SHADA_MPACK_FREE_SPACE (4 * MPACK_ITEM_SIZE) + +static void shada_check_buffer(PackerBuffer *packer) +{ + if (mpack_remaining(packer) < SHADA_MPACK_FREE_SPACE) { + packer->packer_flush(packer); + } +} + +static uint32_t additional_data_len(AdditionalData *src) +{ + return src ? src->nitems : 0; +} + +static void dump_additional_data(AdditionalData *src, PackerBuffer *sbuf) +{ + if (src != NULL) { + mpack_raw(src->data, src->nbytes, sbuf); + } +} /// Write single ShaDa entry /// @@ -1490,145 +1346,93 @@ static char *shada_filename(const char *file) /// restrictions. /// /// @return kSDWriteSuccessful, kSDWriteFailed or kSDWriteIgnError. -static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntry entry, +static ShaDaWriteResult shada_pack_entry(PackerBuffer *const packer, ShadaEntry entry, const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL { ShaDaWriteResult ret = kSDWriteFailed; - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer *spacker = msgpack_packer_new(&sbuf, &msgpack_sbuffer_write); -#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), \ - _("additional elements of ShaDa " what)) \ - == FAIL) { \ - goto shada_pack_entry_error; \ - } \ - }); \ - } \ - } while (0) -#define DUMP_ADDITIONAL_DATA(src, what) \ - do { \ - dict_T *const d = (src); \ - if (d != NULL) { \ - size_t todo = d->dv_hashtab.ht_used; \ - for (const hashitem_T *hi = d->dv_hashtab.ht_array; todo; hi++) { \ - if (!HASHITEM_EMPTY(hi)) { \ - todo--; \ - dictitem_T *const di = TV_DICT_HI2DI(hi); \ - 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, \ - _("additional data of ShaDa " what)) \ - == FAIL) { \ - goto shada_pack_entry_error; \ - } \ - } \ - } \ - } \ - } while (0) + PackerBuffer sbuf = packer_string_buffer(); + #define CHECK_DEFAULT(entry, attr) \ (sd_default_values[(entry).type].data.attr == (entry).data.attr) #define ONE_IF_NOT_DEFAULT(entry, attr) \ - ((size_t)(!CHECK_DEFAULT(entry, attr))) + ((uint32_t)(!CHECK_DEFAULT(entry, attr))) #define PACK_BOOL(entry, name, attr) \ do { \ if (!CHECK_DEFAULT(entry, search_pattern.attr)) { \ - PACK_STATIC_STR(name); \ - if (sd_default_values[(entry).type].data.search_pattern.attr) { \ - msgpack_pack_false(spacker); \ - } else { \ - msgpack_pack_true(spacker); \ - } \ + PACK_KEY(name); \ + mpack_bool(&sbuf.ptr, !sd_default_values[(entry).type].data.search_pattern.attr); \ } \ } while (0) + + shada_check_buffer(&sbuf); switch (entry.type) { case kSDItemMissing: abort(); case kSDItemUnknown: - if (spacker->callback(spacker->data, entry.data.unknown_item.contents, - (unsigned)entry.data.unknown_item.size) == -1) { - goto shada_pack_entry_error; - } + mpack_raw(entry.data.unknown_item.contents, entry.data.unknown_item.size, &sbuf); break; case kSDItemHistoryEntry: { const bool is_hist_search = entry.data.history_item.histtype == HIST_SEARCH; - const size_t arr_size = 2 + (size_t)is_hist_search + (size_t)( - tv_list_len(entry.data. - history_item. - additional_elements)); - msgpack_pack_array(spacker, arr_size); - msgpack_pack_uint8(spacker, entry.data.history_item.histtype); - PACK_BIN(cstr_as_string(entry.data.history_item.string)); + uint32_t arr_size = (2 + (uint32_t)is_hist_search + + additional_data_len(entry.additional_data)); + mpack_array(&sbuf.ptr, arr_size); + mpack_uint(&sbuf.ptr, entry.data.history_item.histtype); + mpack_bin(cstr_as_string(entry.data.history_item.string), &sbuf); if (is_hist_search) { - msgpack_pack_uint8(spacker, (uint8_t)entry.data.history_item.sep); + mpack_uint(&sbuf.ptr, (uint8_t)entry.data.history_item.sep); } - DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements, - "history entry item"); + dump_additional_data(entry.additional_data, &sbuf); break; } case kSDItemVariable: { - if (entry.data.global_var.value.v_type == VAR_BLOB) { - // Strings and Blobs both pack as msgpack BINs; differentiate them by - // storing an additional VAR_TYPE_BLOB element alongside Blobs - list_T *const list = tv_list_alloc(1); - tv_list_append_number(list, VAR_TYPE_BLOB); - entry.data.global_var.additional_elements = list; - } - const size_t arr_size = 2 + (size_t)(tv_list_len(entry.data.global_var.additional_elements)); - msgpack_pack_array(spacker, arr_size); + bool is_blob = (entry.data.global_var.value.v_type == VAR_BLOB); + uint32_t arr_size = 2 + (is_blob ? 1 : 0) + additional_data_len(entry.additional_data); + mpack_array(&sbuf.ptr, arr_size); const String varname = cstr_as_string(entry.data.global_var.name); - PACK_BIN(varname); + mpack_bin(varname, &sbuf); 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"), entry.data.global_var.name); goto shada_pack_entry_error; } - DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements, - "variable item"); + if (is_blob) { + mpack_check_buffer(&sbuf); + mpack_integer(&sbuf.ptr, VAR_TYPE_BLOB); + } + dump_additional_data(entry.additional_data, &sbuf); break; } case kSDItemSubString: { - const size_t arr_size = 1 + (size_t)( - tv_list_len(entry.data.sub_string.additional_elements)); - msgpack_pack_array(spacker, arr_size); - PACK_BIN(cstr_as_string(entry.data.sub_string.sub)); - DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements, - "sub string item"); + uint32_t arr_size = 1 + additional_data_len(entry.additional_data); + mpack_array(&sbuf.ptr, arr_size); + mpack_bin(cstr_as_string(entry.data.sub_string.sub), &sbuf); + dump_additional_data(entry.additional_data, &sbuf); break; } case kSDItemSearchPattern: { - size_t entry_map_size = ( - 1 // Search pattern is always present - + ONE_IF_NOT_DEFAULT(entry, search_pattern.magic) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.is_last_used) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.smartcase) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.has_line_offset) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.place_cursor_at_end) - + ONE_IF_NOT_DEFAULT(entry, - search_pattern.is_substitute_pattern) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.highlighted) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.offset) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.search_backward) - // finally, additional data: - + ( - entry.data.search_pattern.additional_data - ? entry.data.search_pattern.additional_data->dv_hashtab.ht_used - : 0)); - msgpack_pack_map(spacker, entry_map_size); - PACK_STATIC_STR(SEARCH_KEY_PAT); - PACK_BIN(cstr_as_string(entry.data.search_pattern.pat)); + uint32_t entry_map_size = (1 // Search pattern is always present + + ONE_IF_NOT_DEFAULT(entry, search_pattern.magic) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.is_last_used) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.smartcase) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.has_line_offset) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.place_cursor_at_end) + + ONE_IF_NOT_DEFAULT(entry, + search_pattern.is_substitute_pattern) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.highlighted) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.offset) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.search_backward) + + additional_data_len(entry.additional_data)); + mpack_map(&sbuf.ptr, entry_map_size); + PACK_KEY(SEARCH_KEY_PAT); + mpack_bin(entry.data.search_pattern.pat, &sbuf); PACK_BOOL(entry, SEARCH_KEY_MAGIC, magic); PACK_BOOL(entry, SEARCH_KEY_IS_LAST_USED, is_last_used); PACK_BOOL(entry, SEARCH_KEY_SMARTCASE, smartcase); @@ -1638,132 +1442,108 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr PACK_BOOL(entry, SEARCH_KEY_HIGHLIGHTED, highlighted); PACK_BOOL(entry, SEARCH_KEY_BACKWARD, search_backward); if (!CHECK_DEFAULT(entry, search_pattern.offset)) { - PACK_STATIC_STR(SEARCH_KEY_OFFSET); - msgpack_pack_int64(spacker, entry.data.search_pattern.offset); + PACK_KEY(SEARCH_KEY_OFFSET); + mpack_integer(&sbuf.ptr, entry.data.search_pattern.offset); } #undef PACK_BOOL - DUMP_ADDITIONAL_DATA(entry.data.search_pattern.additional_data, - "search pattern item"); + dump_additional_data(entry.additional_data, &sbuf); break; } case kSDItemChange: case kSDItemGlobalMark: case kSDItemLocalMark: case kSDItemJump: { - size_t entry_map_size = ( - 1 // File name + size_t entry_map_size = (1 // File name + ONE_IF_NOT_DEFAULT(entry, filemark.mark.lnum) + ONE_IF_NOT_DEFAULT(entry, filemark.mark.col) + ONE_IF_NOT_DEFAULT(entry, filemark.name) - // Additional entries, if any: - + ( - entry.data.filemark.additional_data == NULL - ? 0 - : entry.data.filemark.additional_data->dv_hashtab.ht_used)); - msgpack_pack_map(spacker, entry_map_size); - PACK_STATIC_STR(KEY_FILE); - PACK_BIN(cstr_as_string(entry.data.filemark.fname)); + + additional_data_len(entry.additional_data)); + mpack_map(&sbuf.ptr, (uint32_t)entry_map_size); + PACK_KEY(KEY_FILE); + mpack_bin(cstr_as_string(entry.data.filemark.fname), &sbuf); if (!CHECK_DEFAULT(entry, filemark.mark.lnum)) { - PACK_STATIC_STR(KEY_LNUM); - msgpack_pack_long(spacker, entry.data.filemark.mark.lnum); + PACK_KEY(KEY_LNUM); + mpack_integer(&sbuf.ptr, entry.data.filemark.mark.lnum); } if (!CHECK_DEFAULT(entry, filemark.mark.col)) { - PACK_STATIC_STR(KEY_COL); - msgpack_pack_long(spacker, entry.data.filemark.mark.col); + PACK_KEY(KEY_COL); + mpack_integer(&sbuf.ptr, entry.data.filemark.mark.col); } assert(entry.type == kSDItemJump || entry.type == kSDItemChange ? CHECK_DEFAULT(entry, filemark.name) : true); if (!CHECK_DEFAULT(entry, filemark.name)) { - PACK_STATIC_STR(KEY_NAME_CHAR); - msgpack_pack_uint8(spacker, (uint8_t)entry.data.filemark.name); + PACK_KEY(KEY_NAME_CHAR); + mpack_uint(&sbuf.ptr, (uint8_t)entry.data.filemark.name); } - DUMP_ADDITIONAL_DATA(entry.data.filemark.additional_data, - "mark (change, jump, global or local) item"); + dump_additional_data(entry.additional_data, &sbuf); break; } case kSDItemRegister: { - size_t entry_map_size = (2 // Register contents and name - + ONE_IF_NOT_DEFAULT(entry, reg.type) - + ONE_IF_NOT_DEFAULT(entry, reg.width) - + ONE_IF_NOT_DEFAULT(entry, reg.is_unnamed) - // Additional entries, if any: - + (entry.data.reg.additional_data == NULL - ? 0 - : entry.data.reg.additional_data->dv_hashtab.ht_used)); - msgpack_pack_map(spacker, entry_map_size); - PACK_STATIC_STR(REG_KEY_CONTENTS); - msgpack_pack_array(spacker, entry.data.reg.contents_size); + uint32_t entry_map_size = (2 // Register contents and name + + ONE_IF_NOT_DEFAULT(entry, reg.type) + + ONE_IF_NOT_DEFAULT(entry, reg.width) + + ONE_IF_NOT_DEFAULT(entry, reg.is_unnamed) + + additional_data_len(entry.additional_data)); + + mpack_map(&sbuf.ptr, entry_map_size); + PACK_KEY(REG_KEY_CONTENTS); + mpack_array(&sbuf.ptr, (uint32_t)entry.data.reg.contents_size); for (size_t i = 0; i < entry.data.reg.contents_size; i++) { - PACK_BIN(cstr_as_string(entry.data.reg.contents[i])); + mpack_bin(cstr_as_string(entry.data.reg.contents[i]), &sbuf); } - PACK_STATIC_STR(KEY_NAME_CHAR); - msgpack_pack_char(spacker, entry.data.reg.name); + PACK_KEY(KEY_NAME_CHAR); + mpack_uint(&sbuf.ptr, (uint8_t)entry.data.reg.name); if (!CHECK_DEFAULT(entry, reg.type)) { - PACK_STATIC_STR(REG_KEY_TYPE); - msgpack_pack_uint8(spacker, (uint8_t)entry.data.reg.type); + PACK_KEY(REG_KEY_TYPE); + mpack_uint(&sbuf.ptr, (uint8_t)entry.data.reg.type); } if (!CHECK_DEFAULT(entry, reg.width)) { - PACK_STATIC_STR(REG_KEY_WIDTH); - msgpack_pack_uint64(spacker, (uint64_t)entry.data.reg.width); + PACK_KEY(REG_KEY_WIDTH); + mpack_uint64(&sbuf.ptr, (uint64_t)entry.data.reg.width); } if (!CHECK_DEFAULT(entry, reg.is_unnamed)) { - PACK_STATIC_STR(REG_KEY_UNNAMED); - if (entry.data.reg.is_unnamed) { - msgpack_pack_true(spacker); - } else { - msgpack_pack_false(spacker); - } + PACK_KEY(REG_KEY_UNNAMED); + mpack_bool(&sbuf.ptr, entry.data.reg.is_unnamed); } - DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data, "register item"); + dump_additional_data(entry.additional_data, &sbuf); break; } case kSDItemBufferList: - msgpack_pack_array(spacker, entry.data.buffer_list.size); + mpack_array(&sbuf.ptr, (uint32_t)entry.data.buffer_list.size); for (size_t i = 0; i < entry.data.buffer_list.size; i++) { - size_t entry_map_size = ( - 1 // Buffer name + size_t entry_map_size = (1 // Buffer name + (size_t)(entry.data.buffer_list.buffers[i].pos.lnum != default_pos.lnum) + (size_t)(entry.data.buffer_list.buffers[i].pos.col != default_pos.col) - // Additional entries, if any: - + ( - entry.data.buffer_list.buffers[i].additional_data - == NULL - ? 0 - : (entry.data.buffer_list.buffers[i].additional_data - ->dv_hashtab.ht_used))); - msgpack_pack_map(spacker, entry_map_size); - PACK_STATIC_STR(KEY_FILE); - PACK_BIN(cstr_as_string(entry.data.buffer_list.buffers[i].fname)); + + additional_data_len(entry.data.buffer_list.buffers[i]. + additional_data)); + mpack_map(&sbuf.ptr, (uint32_t)entry_map_size); + PACK_KEY(KEY_FILE); + mpack_bin(cstr_as_string(entry.data.buffer_list.buffers[i].fname), &sbuf); if (entry.data.buffer_list.buffers[i].pos.lnum != 1) { - PACK_STATIC_STR(KEY_LNUM); - msgpack_pack_uint64(spacker, (uint64_t)entry.data.buffer_list.buffers[i].pos.lnum); + PACK_KEY(KEY_LNUM); + mpack_uint64(&sbuf.ptr, (uint64_t)entry.data.buffer_list.buffers[i].pos.lnum); } if (entry.data.buffer_list.buffers[i].pos.col != 0) { - PACK_STATIC_STR(KEY_COL); - msgpack_pack_uint64(spacker, (uint64_t)entry.data.buffer_list.buffers[i].pos.col); + PACK_KEY(KEY_COL); + mpack_uint64(&sbuf.ptr, (uint64_t)entry.data.buffer_list.buffers[i].pos.col); } - DUMP_ADDITIONAL_DATA(entry.data.buffer_list.buffers[i].additional_data, - "buffer list subitem"); + dump_additional_data(entry.data.buffer_list.buffers[i].additional_data, &sbuf); } break; case kSDItemHeader: - msgpack_pack_map(spacker, entry.data.header.size); + mpack_map(&sbuf.ptr, (uint32_t)entry.data.header.size); for (size_t i = 0; i < entry.data.header.size; i++) { - const String s = entry.data.header.items[i].key; - msgpack_pack_str(spacker, s.size); - if (s.size) { - msgpack_pack_str_body(spacker, s.data, s.size); - } + mpack_str(entry.data.header.items[i].key, &sbuf); const Object obj = entry.data.header.items[i].value; switch (obj.type) { case kObjectTypeString: - PACK_BIN(obj.data.string); + mpack_bin(obj.data.string, &sbuf); break; case kObjectTypeInteger: - msgpack_pack_int64(spacker, (int64_t)obj.data.integer); + mpack_integer(&sbuf.ptr, obj.data.integer); break; default: abort(); @@ -1773,33 +1553,28 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr } #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) { + shada_check_buffer(packer); + if (entry.type == kSDItemUnknown) { - if (msgpack_pack_uint64(packer, entry.data.unknown_item.type) == -1) { - goto shada_pack_entry_error; - } + mpack_uint64(&packer->ptr, entry.data.unknown_item.type); } else { - if (msgpack_pack_uint64(packer, (uint64_t)entry.type) == -1) { - goto shada_pack_entry_error; - } + mpack_uint64(&packer->ptr, (uint64_t)entry.type); } - if (msgpack_pack_uint64(packer, (uint64_t)entry.timestamp) == -1) { - goto shada_pack_entry_error; + mpack_uint64(&packer->ptr, (uint64_t)entry.timestamp); + if (packed.size > 0) { + mpack_uint64(&packer->ptr, (uint64_t)packed.size); + mpack_raw(packed.data, packed.size, packer); } - if (sbuf.size > 0) { - if ((msgpack_pack_uint64(packer, (uint64_t)sbuf.size) == -1) - || (packer->callback(packer->data, sbuf.data, - (unsigned)sbuf.size) == -1)) { - goto shada_pack_entry_error; - } + + if (packer->anyint != 0) { // error code + goto shada_pack_entry_error; } } - msgpack_packer_free(spacker); - msgpack_sbuffer_destroy(&sbuf); - return kSDWriteSuccessful; + ret = kSDWriteSuccessful; shada_pack_entry_error: - msgpack_packer_free(spacker); - msgpack_sbuffer_destroy(&sbuf); + xfree(sbuf.startptr); return ret; } @@ -1811,7 +1586,7 @@ shada_pack_entry_error: /// @param[in] entry Entry written. /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// restrictions. -static inline ShaDaWriteResult shada_pack_pfreed_entry(msgpack_packer *const packer, +static inline ShaDaWriteResult shada_pack_pfreed_entry(PackerBuffer *const packer, PossiblyFreedShadaEntry entry, const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE @@ -1849,76 +1624,29 @@ static int compare_file_marks(const void *a, const void *b) /// /// @return kSDReadStatusNotShaDa, kSDReadStatusReadError or /// kSDReadStatusSuccess. -static inline ShaDaReadResult shada_parse_msgpack(ShaDaReadDef *const sd_reader, - const size_t length, - msgpack_unpacked *ret_unpacked, - char **const ret_buf) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) +static ShaDaReadResult shada_check_status(uintmax_t initial_fpos, int status, size_t remaining) + FUNC_ATTR_WARN_UNUSED_RESULT { - const uintmax_t initial_fpos = sd_reader->fpos; - char *const buf = xmalloc(length); - - const ShaDaReadResult fl_ret = fread_len(sd_reader, buf, length); - if (fl_ret != kSDReadStatusSuccess) { - xfree(buf); - return fl_ret; - } - bool did_try_to_free = false; -shada_parse_msgpack_read_next: {} - size_t off = 0; - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - const msgpack_unpack_return result = - msgpack_unpack_next(&unpacked, buf, length, &off); - ShaDaReadResult ret = kSDReadStatusSuccess; - switch (result) { - case MSGPACK_UNPACK_SUCCESS: - if (off < length) { - goto shada_parse_msgpack_extra_bytes; - } - break; - case MSGPACK_UNPACK_PARSE_ERROR: - semsg(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error " - "at position %" PRIu64), - (uint64_t)initial_fpos); - ret = kSDReadStatusNotShaDa; - break; - case MSGPACK_UNPACK_NOMEM_ERROR: - if (!did_try_to_free) { - did_try_to_free = true; - try_to_free_memory(); - goto shada_parse_msgpack_read_next; + switch (status) { + case MPACK_OK: + if (remaining) { + semsg(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string " + "at position %" PRIu64), + (uint64_t)initial_fpos); + return kSDReadStatusNotShaDa; } - emsg(_(e_outofmem)); - ret = kSDReadStatusReadError; - break; - case MSGPACK_UNPACK_CONTINUE: + return kSDReadStatusSuccess; + case MPACK_EOF: semsg(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string " "at position %" PRIu64), (uint64_t)initial_fpos); - ret = kSDReadStatusNotShaDa; - break; - case MSGPACK_UNPACK_EXTRA_BYTES: -shada_parse_msgpack_extra_bytes: - semsg(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string " + return kSDReadStatusNotShaDa; + default: + semsg(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error " "at position %" PRIu64), (uint64_t)initial_fpos); - ret = kSDReadStatusNotShaDa; - break; - } - if (ret_buf != NULL && ret == kSDReadStatusSuccess) { - if (ret_unpacked == NULL) { - msgpack_unpacked_destroy(&unpacked); - } else { - *ret_unpacked = unpacked; - } - *ret_buf = buf; - } else { - assert(ret_buf == NULL || ret != kSDReadStatusSuccess); - msgpack_unpacked_destroy(&unpacked); - xfree(buf); + return kSDReadStatusNotShaDa; } - return ret; } /// Format shada entry for debugging purposes @@ -1935,25 +1663,16 @@ static const char *shada_format_entry(const ShadaEntry entry) // ^ Space for `can_free_entry` #define FORMAT_MARK_ENTRY(entry_name, name_fmt, name_fmt_arg) \ do { \ - typval_T ad_tv = { \ - .v_type = VAR_DICT, \ - .vval.v_dict = entry.data.filemark.additional_data \ - }; \ - size_t ad_len; \ - char *const ad = encode_tv2string(&ad_tv, &ad_len); \ vim_snprintf_add(S_LEN(ret), \ entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \ "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \ - "ad={%p:[%zu]%.64s} }", \ + "}", \ name_fmt_arg, \ strlen(entry.data.filemark.fname), \ entry.data.filemark.fname, \ entry.data.filemark.mark.lnum, \ entry.data.filemark.mark.col, \ - entry.data.filemark.mark.coladd, \ - (void *)entry.data.filemark.additional_data, \ - ad_len, \ - ad); \ + entry.data.filemark.mark.coladd); \ } while (0) switch (entry.type) { case kSDItemMissing: @@ -2021,11 +1740,11 @@ static const char *shada_format_pfreed_entry(const PossiblyFreedShadaEntry pfs_e /// @param[in,out] ret_wms Location where results are saved. /// @param[out] packer MessagePack packer for entries which are not /// merged. -static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_reader, +static inline ShaDaWriteResult shada_read_when_writing(FileDescriptor *const sd_reader, const unsigned srni_flags, const size_t max_kbyte, WriteMergerState *const wms, - msgpack_packer *const packer) + PackerBuffer *const packer) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { ShaDaWriteResult ret = kSDWriteSuccessful; @@ -2137,8 +1856,8 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re } // Ignore duplicates. if (wms_entry.timestamp == entry.timestamp - && (wms_entry.data.filemark.additional_data == NULL - && entry.data.filemark.additional_data == NULL) + && (wms_entry.additional_data == NULL + && entry.additional_data == NULL) && marks_equal(wms_entry.data.filemark.mark, entry.data.filemark.mark) && strcmp(wms_entry.data.filemark.fname, @@ -2167,13 +1886,18 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re shada_free_shada_entry(&entry); break; } - if (wms->global_marks[idx].data.type == kSDItemMissing) { + + // Global or numbered mark. + PossiblyFreedShadaEntry *mark + = idx < 26 ? &wms->global_marks[idx] : &wms->numbered_marks[idx - 26]; + + if (mark->data.type == kSDItemMissing) { if (namedfm[idx].fmark.timestamp >= entry.timestamp) { shada_free_shada_entry(&entry); break; } } - COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry); + COMPARE_WITH_ENTRY(mark, entry); } break; case kSDItemChange: @@ -2366,11 +2090,11 @@ static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, .is_substitute_pattern = is_substitute_pattern, .highlighted = ((is_substitute_pattern ^ search_last_used) && search_highlighted), - .pat = pat.pat, - .additional_data = pat.additional_data, + .pat = cstr_as_string(pat.pat), .search_backward = (!is_substitute_pattern && pat.off.dir == '?'), } - } + }, + .additional_data = pat.additional_data, } }; } @@ -2407,11 +2131,11 @@ static inline void shada_initialize_registers(WriteMergerState *const wms, int m .contents_size = reg.y_size, .type = reg.y_type, .width = (size_t)(reg.y_type == kMTBlockWise ? reg.y_width : 0), - .additional_data = reg.additional_data, .name = name, .is_unnamed = is_unnamed, } - } + }, + .additional_data = reg.additional_data, } }; } while (reg_iter != ITER_REGISTER_NULL); @@ -2477,13 +2201,37 @@ static int hist_type2char(const int type) return NUL; } +static PackerBuffer packer_buffer_for_file(FileDescriptor *file) +{ + if (file_space(file) < SHADA_MPACK_FREE_SPACE) { + file_flush(file); + } + return (PackerBuffer) { + .startptr = file->buffer, + .ptr = file->write_pos, + .endptr = file->buffer + ARENA_BLOCK_SIZE, + .anydata = file, + .anyint = 0, // set to nonzero if error + .packer_flush = flush_file_buffer, + }; +} + +static void flush_file_buffer(PackerBuffer *buffer) +{ + FileDescriptor *fd = buffer->anydata; + fd->write_pos = buffer->ptr; + buffer->anyint = file_flush(fd); + buffer->ptr = fd->write_pos; +} + /// Write ShaDa file /// /// @param[in] sd_writer Structure containing file writer definition. /// @param[in] sd_reader Structure containing file reader definition. If it is /// not NULL then contents of this file will be merged /// with current Neovim runtime. -static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDef *const sd_reader) +static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, + FileDescriptor *const sd_reader) FUNC_ATTR_NONNULL_ARG(1) { ShaDaWriteResult ret = kSDWriteSuccessful; @@ -2524,8 +2272,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe } } - const unsigned srni_flags = (unsigned)( - kSDReadUndisableableData + const unsigned srni_flags = (unsigned)(kSDReadUndisableableData | kSDReadUnknown | (dump_history ? kSDReadHistory : 0) | (dump_registers ? kSDReadRegisters : 0) @@ -2534,8 +2281,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe | (num_marked_files ? kSDReadLocalMarks | kSDReadChanges : 0)); - msgpack_packer *const packer = msgpack_packer_new(sd_writer, - &msgpack_sd_writer_write); + PackerBuffer packer = packer_buffer_for_file(sd_writer); // Set b_last_cursor for all the buffers that have a window. // @@ -2549,7 +2295,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe find_removable_bufs(&removable_bufs); // Write header - if (shada_pack_entry(packer, (ShadaEntry) { + if (shada_pack_entry(&packer, (ShadaEntry) { .type = kSDItemHeader, .timestamp = os_time(), .data = { @@ -2578,7 +2324,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe // Write buffer list if (find_shada_parameter('%') != NULL) { ShadaEntry buflist_entry = shada_get_buflist(&removable_bufs); - if (shada_pack_entry(packer, buflist_entry, 0) == kSDWriteFailed) { + if (shada_pack_entry(&packer, buflist_entry, 0) == kSDWriteFailed) { xfree(buflist_entry.data.buffer_list.buffers); ret = kSDWriteFailed; goto shada_write_exit; @@ -2628,16 +2374,16 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe typval_T tgttv; tv_copy(&vartv, &tgttv); ShaDaWriteResult spe_ret; - if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { + if ((spe_ret = shada_pack_entry(&packer, (ShadaEntry) { .type = kSDItemVariable, .timestamp = cur_timestamp, .data = { .global_var = { .name = (char *)name, .value = tgttv, - .additional_elements = NULL, } - } + }, + .additional_data = NULL, }, max_kbyte)) == kSDWriteFailed) { tv_clear(&vartv); tv_clear(&tgttv); @@ -2679,9 +2425,9 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe .data = { .sub_string = { .sub = sub.sub, - .additional_elements = sub.additional_elements, } - } + }, + .additional_data = sub.additional_data, } }; } @@ -2721,10 +2467,10 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe .filemark = { .mark = fm.fmark.mark, .name = name, - .additional_data = fm.fmark.additional_data, .fname = (char *)fname, } - } + }, + .additional_data = fm.fmark.additional_data, }, }; if (ascii_isdigit(name)) { @@ -2770,9 +2516,9 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe .mark = fm.mark, .name = name, .fname = (char *)fname, - .additional_data = fm.additional_data, } - } + }, + .additional_data = fm.additional_data, } }; if (fm.timestamp > filemarks->greatest_timestamp) { @@ -2790,9 +2536,9 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe .filemark = { .mark = fm.mark, .fname = (char *)fname, - .additional_data = fm.additional_data, } - } + }, + .additional_data = fm.additional_data, } }; if (fm.timestamp > filemarks->greatest_timestamp) { @@ -2805,7 +2551,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe if (sd_reader != NULL) { const ShaDaWriteResult srww_ret = shada_read_when_writing(sd_reader, srni_flags, max_kbyte, wms, - packer); + &packer); if (srww_ret != kSDWriteSuccessful) { ret = srww_ret; } @@ -2823,10 +2569,10 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe .filemark = { .mark = curwin->w_cursor, .name = '0', - .additional_data = NULL, .fname = curbuf->b_ffname, } - } + }, + .additional_data = NULL, }, }); } @@ -2836,7 +2582,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe do { \ for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \ if ((wms_array)[i_].data.type != kSDItemMissing) { \ - if (shada_pack_pfreed_entry(packer, (wms_array)[i_], max_kbyte) \ + if (shada_pack_pfreed_entry(&packer, (wms_array)[i_], max_kbyte) \ == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ @@ -2848,7 +2594,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe PACK_WMS_ARRAY(wms->numbered_marks); PACK_WMS_ARRAY(wms->registers); for (size_t i = 0; i < wms->jumps_size; i++) { - if (shada_pack_pfreed_entry(packer, wms->jumps[i], max_kbyte) + if (shada_pack_pfreed_entry(&packer, wms->jumps[i], max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; @@ -2857,7 +2603,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe #define PACK_WMS_ENTRY(wms_entry) \ do { \ if ((wms_entry).data.type != kSDItemMissing) { \ - if (shada_pack_pfreed_entry(packer, wms_entry, max_kbyte) \ + if (shada_pack_pfreed_entry(&packer, wms_entry, max_kbyte) \ == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ @@ -2883,14 +2629,14 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe for (size_t i = 0; i < file_markss_to_dump; i++) { PACK_WMS_ARRAY(all_file_markss[i]->marks); for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) { - if (shada_pack_pfreed_entry(packer, all_file_markss[i]->changes[j], + if (shada_pack_pfreed_entry(&packer, all_file_markss[i]->changes[j], max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; } } for (size_t j = 0; j < all_file_markss[i]->additional_marks_size; j++) { - if (shada_pack_entry(packer, all_file_markss[i]->additional_marks[j], + if (shada_pack_entry(&packer, all_file_markss[i]->additional_marks[j], 0) == kSDWriteFailed) { shada_free_shada_entry(&all_file_markss[i]->additional_marks[j]); ret = kSDWriteFailed; @@ -2908,7 +2654,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, ShaDaReadDe if (dump_one_history[i]) { hms_insert_whole_neovim_history(&wms->hms[i]); HMS_ITER(&wms->hms[i], cur_entry, { - if (shada_pack_pfreed_entry(packer, (PossiblyFreedShadaEntry) { + if (shada_pack_pfreed_entry(&packer, (PossiblyFreedShadaEntry) { .data = cur_entry->data, .can_free_entry = cur_entry->can_free_entry, }, max_kbyte) == kSDWriteFailed) { @@ -2934,13 +2680,13 @@ shada_write_exit: }) map_destroy(cstr_t, &wms->file_marks); set_destroy(ptr_t, &removable_bufs); - msgpack_packer_free(packer); + packer.packer_flush(&packer); set_destroy(cstr_t, &wms->dumped_variables); xfree(wms); return ret; } -#undef PACK_STATIC_STR +#undef PACK_KEY /// Write ShaDa file to a given location /// @@ -2958,12 +2704,13 @@ int shada_write_file(const char *const file, bool nomerge) char *const fname = shada_filename(file); char *tempname = NULL; FileDescriptor sd_writer; - ShaDaReadDef sd_reader = { .close = NULL }; + FileDescriptor sd_reader; bool did_open_writer = false; + bool did_open_reader = false; if (!nomerge) { int error; - if ((error = open_shada_file_for_reading(fname, &sd_reader)) != 0) { + if ((error = file_open(&sd_reader, fname, kFileReadOnly, 0)) != 0) { if (error != UV_ENOENT) { semsg(_(SERR "System error while opening ShaDa file %s for reading " "to merge before writing it: %s"), @@ -2973,6 +2720,8 @@ int shada_write_file(const char *const file, bool nomerge) } nomerge = true; goto shada_write_file_nomerge; + } else { + did_open_reader = true; } tempname = modname(fname, ".tmp.a", false); if (tempname == NULL) { @@ -3001,8 +2750,9 @@ shada_write_file_open: {} fname); xfree(fname); xfree(tempname); - assert(sd_reader.close != NULL); - sd_reader.close(&sd_reader); + if (did_open_reader) { + close_file(&sd_reader); + } return FAIL; } (*wp)++; @@ -3047,8 +2797,8 @@ shada_write_file_nomerge: {} if (!did_open_writer) { xfree(fname); xfree(tempname); - if (sd_reader.cookie != NULL) { - sd_reader.close(&sd_reader); + if (did_open_reader) { + close_file(&sd_reader); } return FAIL; } @@ -3062,7 +2812,9 @@ shada_write_file_nomerge: {} const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge ? NULL : &sd_reader)); assert(sw_ret != kSDWriteIgnError); if (!nomerge) { - sd_reader.close(&sd_reader); + if (did_open_reader) { + close_file(&sd_reader); + } bool did_remove = false; if (sw_ret == kSDWriteSuccessful) { FileInfo old_info; @@ -3164,47 +2916,42 @@ static void shada_free_shada_entry(ShadaEntry *const entry) xfree(entry->data.unknown_item.contents); break; case kSDItemHeader: - api_free_dictionary(entry->data.header); + api_free_dict(entry->data.header); break; case kSDItemChange: case kSDItemJump: case kSDItemGlobalMark: case kSDItemLocalMark: - tv_dict_unref(entry->data.filemark.additional_data); xfree(entry->data.filemark.fname); break; case kSDItemSearchPattern: - tv_dict_unref(entry->data.search_pattern.additional_data); - xfree(entry->data.search_pattern.pat); + api_free_string(entry->data.search_pattern.pat); break; case kSDItemRegister: - tv_dict_unref(entry->data.reg.additional_data); for (size_t i = 0; i < entry->data.reg.contents_size; i++) { xfree(entry->data.reg.contents[i]); } xfree(entry->data.reg.contents); break; case kSDItemHistoryEntry: - tv_list_unref(entry->data.history_item.additional_elements); xfree(entry->data.history_item.string); break; case kSDItemVariable: - tv_list_unref(entry->data.global_var.additional_elements); xfree(entry->data.global_var.name); tv_clear(&entry->data.global_var.value); break; case kSDItemSubString: - tv_list_unref(entry->data.sub_string.additional_elements); xfree(entry->data.sub_string.sub); break; case kSDItemBufferList: for (size_t i = 0; i < entry->data.buffer_list.size; i++) { xfree(entry->data.buffer_list.buffers[i].fname); - tv_dict_unref(entry->data.buffer_list.buffers[i].additional_data); + xfree(entry->data.buffer_list.buffers[i].additional_data); } xfree(entry->data.buffer_list.buffers); break; } + XFREE_CLEAR(entry->additional_data); } #ifndef HAVE_BE64TOH @@ -3235,18 +2982,18 @@ static inline uint64_t be64toh(uint64_t big_endian_64_bits) /// @return kSDReadStatusSuccess if everything was OK, kSDReadStatusNotShaDa if /// there were not enough bytes to read or kSDReadStatusReadError if /// there was some error while reading. -static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, char *const buffer, +static ShaDaReadResult fread_len(FileDescriptor *const sd_reader, char *const buffer, const size_t length) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - const ptrdiff_t read_bytes = sd_reader->read(sd_reader, buffer, length); + const ptrdiff_t read_bytes = file_read(sd_reader, buffer, length); + if (read_bytes < 0) { + semsg(_(SERR "System error while reading ShaDa file: %s"), + os_strerror((int)read_bytes)); + return kSDReadStatusReadError; + } if (read_bytes != (ptrdiff_t)length) { - if (sd_reader->error != NULL) { - semsg(_(SERR "System error while reading ShaDa file: %s"), - sd_reader->error); - return kSDReadStatusReadError; - } semsg(_(RCERR "Error while reading ShaDa file: " "last entry specified that it occupies %" PRIu64 " bytes, " "but file ended earlier"), @@ -3272,26 +3019,32 @@ static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, char *const buff /// @return kSDReadStatusSuccess if reading was successful, /// kSDReadStatusNotShaDa if there were not enough bytes to read or /// kSDReadStatusReadError if reading failed for whatever reason. -static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const int first_char, +/// kSDReadStatusFinished if eof and that was allowed +static ShaDaReadResult msgpack_read_uint64(FileDescriptor *const sd_reader, bool allow_eof, uint64_t *const result) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - const uintmax_t fpos = sd_reader->fpos - 1; - - if (first_char == EOF) { - if (sd_reader->error) { - semsg(_(SERR "System error while reading integer from ShaDa file: %s"), - sd_reader->error); - return kSDReadStatusReadError; - } else if (sd_reader->eof) { - semsg(_(RCERR "Error while reading ShaDa file: " - "expected positive integer at position %" PRIu64 - ", but got nothing"), - (uint64_t)fpos); - return kSDReadStatusNotShaDa; + const uintmax_t fpos = sd_reader->bytes_read; + + uint8_t ret; + ptrdiff_t read_bytes = file_read(sd_reader, (char *)&ret, 1); + + if (read_bytes < 0) { + semsg(_(SERR "System error while reading integer from ShaDa file: %s"), + os_strerror((int)read_bytes)); + return kSDReadStatusReadError; + } else if (read_bytes == 0) { + if (allow_eof && file_eof(sd_reader)) { + return kSDReadStatusFinished; } + semsg(_(RCERR "Error while reading ShaDa file: " + "expected positive integer at position %" PRIu64 + ", but got nothing"), + (uint64_t)fpos); + return kSDReadStatusNotShaDa; } + int first_char = (int)ret; if (~first_char & 0x80) { // Positive fixnum *result = (uint64_t)((uint8_t)first_char); @@ -3332,128 +3085,6 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const RERR "Error while reading ShaDa file: " \ entry_name " entry at position %" PRIu64 " " \ error_desc -#define CHECK_KEY(key, \ - expected) ((key).via.str.size == (sizeof(expected) - 1) \ - && strncmp((key).via.str.ptr, expected, (sizeof(expected) - 1)) == 0) -#define CLEAR_GA_AND_ERROR_OUT(ga) \ - do { \ - ga_clear(&(ga)); \ - goto shada_read_next_item_error; \ - } while (0) -#define ID(s) s -#define BINDUP(b) xmemdupz((b).ptr, (b).size) -#define TOINT(s) ((int)(s)) -#define TOCHAR(s) ((char)(s)) -#define TOU8(s) ((uint8_t)(s)) -#define TOSIZE(s) ((size_t)(s)) -#define CHECKED_ENTRY(condition, error_desc, entry_name, obj, tgt, attr, \ - proc) \ - do { \ - if (!(condition)) { \ - semsg(_(READERR(entry_name, error_desc)), initial_fpos); \ - CLEAR_GA_AND_ERROR_OUT(ad_ga); \ - } \ - (tgt) = proc((obj).via.attr); \ - } while (0) -#define CHECK_KEY_IS_STR(un, entry_name) \ - if ((un).data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \ - semsg(_(READERR(entry_name, "has key which is not a string")), \ - initial_fpos); \ - CLEAR_GA_AND_ERROR_OUT(ad_ga); \ - } else if ((un).data.via.map.ptr[i].key.via.str.size == 0) { \ - semsg(_(READERR(entry_name, "has empty key")), initial_fpos); \ - CLEAR_GA_AND_ERROR_OUT(ad_ga); \ - } -#define CHECKED_KEY(un, entry_name, name, error_desc, tgt, condition, attr, proc) \ - else if (CHECK_KEY((un).data.via.map.ptr[i].key, name)) \ - { \ - CHECKED_ENTRY(condition, \ - "has " name " key value " error_desc, \ - entry_name, \ - (un).data.via.map.ptr[i].val, \ - tgt, \ - attr, \ - proc); \ - } -#define TYPED_KEY(un, entry_name, name, type_name, tgt, objtype, attr, proc) \ - CHECKED_KEY(un, entry_name, name, "which is not " type_name, tgt, \ - (un).data.via.map.ptr[i].val.type == MSGPACK_OBJECT_##objtype, \ - attr, proc) -#define BOOLEAN_KEY(un, entry_name, name, tgt) \ - TYPED_KEY(un, entry_name, name, "a boolean", tgt, BOOLEAN, boolean, ID) -#define STRING_KEY(un, entry_name, name, tgt) \ - TYPED_KEY(un, entry_name, name, "a binary", tgt, BIN, bin, BINDUP) -#define CONVERTED_STRING_KEY(un, entry_name, name, tgt) \ - TYPED_KEY(un, entry_name, name, "a binary", tgt, BIN, bin, \ - BIN_CONVERTED) -#define INT_KEY(un, entry_name, name, tgt, proc) \ - CHECKED_KEY(un, entry_name, name, "which is not an integer", tgt, \ - (((un).data.via.map.ptr[i].val.type \ - == MSGPACK_OBJECT_POSITIVE_INTEGER) \ - || ((un).data.via.map.ptr[i].val.type \ - == MSGPACK_OBJECT_NEGATIVE_INTEGER)), \ - i64, proc) -#define INTEGER_KEY(un, entry_name, name, tgt) \ - INT_KEY(un, entry_name, name, tgt, TOINT) -#define ADDITIONAL_KEY(un) \ - else { \ - ga_grow(&ad_ga, 1); \ - memcpy(((char *)ad_ga.ga_data) + ((size_t)ad_ga.ga_len \ - * sizeof(*(un).data.via.map.ptr)), \ - (un).data.via.map.ptr + i, \ - sizeof(*(un).data.via.map.ptr)); \ - ad_ga.ga_len++; \ - } -#define BIN_CONVERTED(b) (xmemdupz(((b).ptr), ((b).size))) -#define SET_ADDITIONAL_DATA(tgt, name) \ - do { \ - if (ad_ga.ga_len) { \ - msgpack_object obj = { \ - .type = MSGPACK_OBJECT_MAP, \ - .via = { \ - .map = { \ - .size = (uint32_t)ad_ga.ga_len, \ - .ptr = ad_ga.ga_data, \ - } \ - } \ - }; \ - typval_T adtv; \ - if (msgpack_to_vim(obj, &adtv) == FAIL \ - || adtv.v_type != VAR_DICT) { \ - semsg(_(READERR(name, \ - "cannot be converted to a Vimscript dictionary")), \ - initial_fpos); \ - ga_clear(&ad_ga); \ - tv_clear(&adtv); \ - goto shada_read_next_item_error; \ - } \ - (tgt) = adtv.vval.v_dict; \ - } \ - ga_clear(&ad_ga); \ - } while (0) -#define SET_ADDITIONAL_ELEMENTS(src, src_maxsize, tgt, name) \ - do { \ - if ((src).size > (size_t)(src_maxsize)) { \ - msgpack_object obj = { \ - .type = MSGPACK_OBJECT_ARRAY, \ - .via = { \ - .array = { \ - .size = ((src).size - (uint32_t)(src_maxsize)), \ - .ptr = (src).ptr + (src_maxsize), \ - } \ - } \ - }; \ - typval_T aetv; \ - if (msgpack_to_vim(obj, &aetv) == FAIL) { \ - semsg(_(READERR(name, "cannot be converted to a Vimscript list")), \ - initial_fpos); \ - tv_clear(&aetv); \ - goto shada_read_next_item_error; \ - } \ - assert(aetv.v_type == VAR_LIST); \ - (tgt) = aetv.vval.v_list; \ - } \ - } while (0) /// Iterate over shada file contents /// @@ -3465,8 +3096,9 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const /// greater then given. /// /// @return Any value from ShaDaReadResult enum. -static ShaDaReadResult shada_read_next_item(ShaDaReadDef *const sd_reader, ShadaEntry *const entry, - const unsigned flags, const size_t max_kbyte) +static ShaDaReadResult shada_read_next_item(FileDescriptor *const sd_reader, + ShadaEntry *const entry, const unsigned flags, + const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { ShaDaReadResult ret = kSDReadStatusMalformed; @@ -3476,29 +3108,30 @@ shada_read_next_item_start: // somebody calls goto shada_read_next_item_error before anything is set in // the switch. CLEAR_POINTER(entry); - if (sd_reader->eof) { + if (file_eof(sd_reader)) { return kSDReadStatusFinished; } + bool verify_but_ignore = false; + // First: manually unpack type, timestamp and length. // This is needed to avoid both seeking and having to maintain a buffer. uint64_t type_u64 = (uint64_t)kSDItemMissing; uint64_t timestamp_u64; uint64_t length_u64; - const uint64_t initial_fpos = (uint64_t)sd_reader->fpos; - const int first_char = read_char(sd_reader); - if (first_char == EOF && sd_reader->eof) { - return kSDReadStatusFinished; - } + const uint64_t initial_fpos = sd_reader->bytes_read; + AdditionalDataBuilder ad = KV_INITIAL_VALUE; + uint32_t read_additional_array_elements = 0; + char *error_alloc = NULL; ShaDaReadResult mru_ret; - if (((mru_ret = msgpack_read_uint64(sd_reader, first_char, &type_u64)) + if (((mru_ret = msgpack_read_uint64(sd_reader, true, &type_u64)) != kSDReadStatusSuccess) - || ((mru_ret = msgpack_read_uint64(sd_reader, read_char(sd_reader), + || ((mru_ret = msgpack_read_uint64(sd_reader, false, ×tamp_u64)) != kSDReadStatusSuccess) - || ((mru_ret = msgpack_read_uint64(sd_reader, read_char(sd_reader), + || ((mru_ret = msgpack_read_uint64(sd_reader, false, &length_u64)) != kSDReadStatusSuccess)) { return mru_ret; @@ -3538,16 +3171,42 @@ shada_read_next_item_start: // in incomplete MessagePack string. if (initial_fpos == 0 && (type_u64 == '\n' || type_u64 > SHADA_LAST_ENTRY)) { - const ShaDaReadResult spm_ret = shada_parse_msgpack(sd_reader, length, - NULL, NULL); - if (spm_ret != kSDReadStatusSuccess) { - return spm_ret; - } + verify_but_ignore = true; } else { const ShaDaReadResult srs_ret = sd_reader_skip(sd_reader, length); if (srs_ret != kSDReadStatusSuccess) { return srs_ret; } + goto shada_read_next_item_start; + } + } + + const uint64_t parse_pos = sd_reader->bytes_read; + bool buf_allocated = false; + // try to avoid allocation for small items which fits entirely + // in the internal buffer of sd_reader + char *buf = file_try_read_buffered(sd_reader, length); + if (!buf) { + buf_allocated = true; + buf = xmalloc(length); + const ShaDaReadResult fl_ret = fread_len(sd_reader, buf, length); + if (fl_ret != kSDReadStatusSuccess) { + ret = fl_ret; + goto shada_read_next_item_error; + } + } + + const char *read_ptr = buf; + size_t read_size = length; + + if (verify_but_ignore) { + int status = unpack_skip(&read_ptr, &read_size); + ShaDaReadResult spm_ret = shada_check_status(parse_pos, status, read_size); + if (buf_allocated) { + xfree(buf); + } + if (spm_ret != kSDReadStatusSuccess) { + return spm_ret; } goto shada_read_next_item_start; } @@ -3557,397 +3216,308 @@ shada_read_next_item_start: entry->data.unknown_item.size = length; entry->data.unknown_item.type = type_u64; if (initial_fpos == 0) { - const ShaDaReadResult spm_ret = shada_parse_msgpack(sd_reader, length, NULL, - &entry->data.unknown_item.contents); + int status = unpack_skip(&read_ptr, &read_size); + ShaDaReadResult spm_ret = shada_check_status(parse_pos, status, read_size); if (spm_ret != kSDReadStatusSuccess) { + if (buf_allocated) { + xfree(buf); + } entry->type = kSDItemMissing; + return spm_ret; } - return spm_ret; } - entry->data.unknown_item.contents = xmalloc(length); - const ShaDaReadResult fl_ret = - fread_len(sd_reader, entry->data.unknown_item.contents, length); - if (fl_ret != kSDReadStatusSuccess) { - shada_free_shada_entry(entry); - entry->type = kSDItemMissing; - } - return fl_ret; + entry->data.unknown_item.contents = buf_allocated ? buf : xmemdup(buf, length); + return kSDReadStatusSuccess; } - msgpack_unpacked unpacked; - char *buf = NULL; - - const ShaDaReadResult spm_ret = shada_parse_msgpack(sd_reader, length, - &unpacked, &buf); - if (spm_ret != kSDReadStatusSuccess) { - ret = spm_ret; - goto shada_read_next_item_error; - } entry->data = sd_default_values[type_u64].data; switch ((ShadaEntryType)type_u64) { case kSDItemHeader: // TODO(bfredl): header is written to file and provides useful debugging // info. It is never read by nvim (earlier we parsed it back to a - // Dictionary, but that value was never used) + // Dict, but that value was never used) break; case kSDItemSearchPattern: { - if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - semsg(_(READERR("search pattern", "is not a dictionary")), - initial_fpos); + Dict(_shada_search_pat) *it = &entry->data.search_pattern; + if (!unpack_keydict(it, DictHash(_shada_search_pat), &ad, &read_ptr, &read_size, + &error_alloc)) { + semsg(_(READERR("search pattern", "%s")), initial_fpos, error_alloc); + it->pat = NULL_STRING; goto shada_read_next_item_error; } - garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); - for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR(unpacked, "search pattern") - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_MAGIC, - entry->data.search_pattern.magic) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_SMARTCASE, - entry->data.search_pattern.smartcase) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HAS_LINE_OFFSET, - entry->data.search_pattern.has_line_offset) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_PLACE_CURSOR_AT_END, - entry->data.search_pattern.place_cursor_at_end) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_IS_LAST_USED, - entry->data.search_pattern.is_last_used) - BOOLEAN_KEY(unpacked, "search pattern", - SEARCH_KEY_IS_SUBSTITUTE_PATTERN, - entry->data.search_pattern.is_substitute_pattern) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HIGHLIGHTED, - entry->data.search_pattern.highlighted) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_BACKWARD, - entry->data.search_pattern.search_backward) - INTEGER_KEY(unpacked, "search pattern", SEARCH_KEY_OFFSET, - entry->data.search_pattern.offset) - CONVERTED_STRING_KEY(unpacked, "search pattern", SEARCH_KEY_PAT, - entry->data.search_pattern.pat) - ADDITIONAL_KEY(unpacked) - } - if (entry->data.search_pattern.pat == NULL) { + + if (!HAS_KEY(it, _shada_search_pat, sp)) { // SEARCH_KEY_PAT semsg(_(READERR("search pattern", "has no pattern")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); + goto shada_read_next_item_error; } - SET_ADDITIONAL_DATA(entry->data.search_pattern.additional_data, - "search pattern"); + entry->data.search_pattern.pat = copy_string(entry->data.search_pattern.pat, NULL); + break; } case kSDItemChange: case kSDItemJump: case kSDItemGlobalMark: case kSDItemLocalMark: { - if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - semsg(_(READERR("mark", "is not a dictionary")), initial_fpos); + Dict(_shada_mark) it = { 0 }; + if (!unpack_keydict(&it, DictHash(_shada_mark), &ad, &read_ptr, &read_size, &error_alloc)) { + semsg(_(READERR("mark", "%s")), initial_fpos, error_alloc); goto shada_read_next_item_error; } - garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); - for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR(unpacked, "mark") - if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, KEY_NAME_CHAR)) { - if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) { - semsg(_(READERR("mark", "has n key which is only valid for " - "local and global mark entries")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - CHECKED_ENTRY((unpacked.data.via.map.ptr[i].val.type - == MSGPACK_OBJECT_POSITIVE_INTEGER), - "has n key value which is not an unsigned integer", - "mark", unpacked.data.via.map.ptr[i].val, - entry->data.filemark.name, u64, TOCHAR); + + if (HAS_KEY(&it, _shada_mark, n)) { + if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) { + semsg(_(READERR("mark", "has n key which is only valid for " + "local and global mark entries")), initial_fpos); + goto shada_read_next_item_error; } - INTEGER_KEY(unpacked, "mark", KEY_LNUM, entry->data.filemark.mark.lnum) - INTEGER_KEY(unpacked, "mark", KEY_COL, entry->data.filemark.mark.col) - STRING_KEY(unpacked, "mark", KEY_FILE, entry->data.filemark.fname) - ADDITIONAL_KEY(unpacked) + entry->data.filemark.name = (char)it.n; + } + + if (HAS_KEY(&it, _shada_mark, l)) { + entry->data.filemark.mark.lnum = (linenr_T)it.l; + } + if (HAS_KEY(&it, _shada_mark, c)) { + entry->data.filemark.mark.col = (colnr_T)it.c; } + if (HAS_KEY(&it, _shada_mark, f)) { + entry->data.filemark.fname = xmemdupz(it.f.data, it.f.size); + } + if (entry->data.filemark.fname == NULL) { semsg(_(READERR("mark", "is missing file name")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); + goto shada_read_next_item_error; } if (entry->data.filemark.mark.lnum <= 0) { semsg(_(READERR("mark", "has invalid line number")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); + goto shada_read_next_item_error; } if (entry->data.filemark.mark.col < 0) { semsg(_(READERR("mark", "has invalid column number")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); + goto shada_read_next_item_error; } - SET_ADDITIONAL_DATA(entry->data.filemark.additional_data, "mark"); break; } case kSDItemRegister: { - if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - semsg(_(READERR("register", "is not a dictionary")), initial_fpos); + Dict(_shada_register) it = { 0 }; + if (!unpack_keydict(&it, DictHash(_shada_register), &ad, &read_ptr, &read_size, &error_alloc)) { + semsg(_(READERR("register", "%s")), initial_fpos, error_alloc); + kv_destroy(it.rc); goto shada_read_next_item_error; } - garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); - for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR(unpacked, "register") - if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, - REG_KEY_CONTENTS)) { - if (unpacked.data.via.map.ptr[i].val.type != MSGPACK_OBJECT_ARRAY) { - semsg(_(READERR("register", - "has " REG_KEY_CONTENTS - " key with non-array value")), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - if (unpacked.data.via.map.ptr[i].val.via.array.size == 0) { - semsg(_(READERR("register", - "has " REG_KEY_CONTENTS " key with empty array")), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - const msgpack_object_array arr = - unpacked.data.via.map.ptr[i].val.via.array; - for (size_t j = 0; j < arr.size; j++) { - if (arr.ptr[j].type != MSGPACK_OBJECT_BIN) { - semsg(_(READERR("register", "has " REG_KEY_CONTENTS " array " - "with non-binary value")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - } - entry->data.reg.contents_size = arr.size; - entry->data.reg.contents = xmalloc(arr.size * sizeof(char *)); - for (size_t j = 0; j < arr.size; j++) { - entry->data.reg.contents[j] = BIN_CONVERTED(arr.ptr[j].via.bin); - } - } - BOOLEAN_KEY(unpacked, "register", REG_KEY_UNNAMED, - entry->data.reg.is_unnamed) - TYPED_KEY(unpacked, "register", REG_KEY_TYPE, "an unsigned integer", - entry->data.reg.type, POSITIVE_INTEGER, u64, TOU8) - TYPED_KEY(unpacked, "register", KEY_NAME_CHAR, "an unsigned integer", - entry->data.reg.name, POSITIVE_INTEGER, u64, TOCHAR) - TYPED_KEY(unpacked, "register", REG_KEY_WIDTH, "an unsigned integer", - entry->data.reg.width, POSITIVE_INTEGER, u64, TOSIZE) - ADDITIONAL_KEY(unpacked) - } - if (entry->data.reg.contents == NULL) { - semsg(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")), + if (it.rc.size == 0) { + semsg(_(READERR("register", + "has " KEY_NAME(REG_KEY_CONTENTS) " key with missing or empty array")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); + goto shada_read_next_item_error; + } + entry->data.reg.contents_size = it.rc.size; + entry->data.reg.contents = xmalloc(it.rc.size * sizeof(char *)); + for (size_t j = 0; j < it.rc.size; j++) { + entry->data.reg.contents[j] = xmemdupz(it.rc.items[j].data, it.rc.items[j].size); } - SET_ADDITIONAL_DATA(entry->data.reg.additional_data, "register"); + kv_destroy(it.rc); + +#define REGISTER_VAL(name, loc, type) \ + if (HAS_KEY(&it, _shada_register, name)) { \ + loc = (type)it.name; \ + } + REGISTER_VAL(REG_KEY_UNNAMED, entry->data.reg.is_unnamed, bool) + REGISTER_VAL(REG_KEY_TYPE, entry->data.reg.type, uint8_t) + REGISTER_VAL(KEY_NAME_CHAR, entry->data.reg.name, char) + REGISTER_VAL(REG_KEY_WIDTH, entry->data.reg.width, size_t) break; } case kSDItemHistoryEntry: { - if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - semsg(_(READERR("history", "is not an array")), initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.size < 2) { - semsg(_(READERR("history", "does not have enough elements")), - initial_fpos); + ssize_t len = unpack_array(&read_ptr, &read_size); + + if (len < 2) { + semsg(_(READERR("history", "is not an array with enough elements")), initial_fpos); goto shada_read_next_item_error; } - if (unpacked.data.via.array.ptr[0].type - != MSGPACK_OBJECT_POSITIVE_INTEGER) { - semsg(_(READERR("history", "has wrong history type type")), - initial_fpos); + Integer hist_type; + if (!unpack_integer(&read_ptr, &read_size, &hist_type)) { + semsg(_(READERR("history", "has wrong history type type")), initial_fpos); goto shada_read_next_item_error; } - if (unpacked.data.via.array.ptr[1].type - != MSGPACK_OBJECT_BIN) { - semsg(_(READERR("history", "has wrong history string type")), - initial_fpos); + const String item = unpack_string(&read_ptr, &read_size); + if (!item.data) { + semsg(_(READERR("history", "has wrong history string type")), initial_fpos); goto shada_read_next_item_error; } - if (memchr(unpacked.data.via.array.ptr[1].via.bin.ptr, 0, - unpacked.data.via.array.ptr[1].via.bin.size) != NULL) { - semsg(_(READERR("history", "contains string with zero byte inside")), - initial_fpos); + if (memchr(item.data, 0, item.size) != NULL) { + semsg(_(READERR("history", "contains string with zero byte inside")), initial_fpos); goto shada_read_next_item_error; } - entry->data.history_item.histtype = - (uint8_t)unpacked.data.via.array.ptr[0].via.u64; - const bool is_hist_search = - entry->data.history_item.histtype == HIST_SEARCH; + entry->data.history_item.histtype = (uint8_t)hist_type; + const bool is_hist_search = entry->data.history_item.histtype == HIST_SEARCH; if (is_hist_search) { - if (unpacked.data.via.array.size < 3) { + if (len < 3) { semsg(_(READERR("search history", "does not have separator character")), initial_fpos); goto shada_read_next_item_error; } - if (unpacked.data.via.array.ptr[2].type - != MSGPACK_OBJECT_POSITIVE_INTEGER) { - semsg(_(READERR("search history", - "has wrong history separator type")), initial_fpos); + Integer sep_type; + if (!unpack_integer(&read_ptr, &read_size, &sep_type)) { + semsg(_(READERR("search history", "has wrong history separator type")), initial_fpos); goto shada_read_next_item_error; } - entry->data.history_item.sep = - (char)unpacked.data.via.array.ptr[2].via.u64; + entry->data.history_item.sep = (char)sep_type; } - size_t strsize; - strsize = ( - unpacked.data.via.array.ptr[1].via.bin.size - + 1 // Zero byte - + 1); // Separator character + size_t strsize = (item.size + + 1 // Zero byte + + 1); // Separator character entry->data.history_item.string = xmalloc(strsize); - memcpy(entry->data.history_item.string, - unpacked.data.via.array.ptr[1].via.bin.ptr, - unpacked.data.via.array.ptr[1].via.bin.size); + memcpy(entry->data.history_item.string, item.data, item.size); entry->data.history_item.string[strsize - 2] = 0; - entry->data.history_item.string[strsize - 1] = - entry->data.history_item.sep; - SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, (2 + is_hist_search), - entry->data.history_item.additional_elements, - "history"); + entry->data.history_item.string[strsize - 1] = entry->data.history_item.sep; + read_additional_array_elements = (uint32_t)(len - (2 + is_hist_search)); break; } case kSDItemVariable: { - if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - semsg(_(READERR("variable", "is not an array")), initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.size < 2) { - semsg(_(READERR("variable", "does not have enough elements")), - initial_fpos); + ssize_t len = unpack_array(&read_ptr, &read_size); + + if (len < 2) { + semsg(_(READERR("variable", "is not an array with enough elements")), initial_fpos); goto shada_read_next_item_error; } - if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { - semsg(_(READERR("variable", "has wrong variable name type")), - initial_fpos); + + String name = unpack_string(&read_ptr, &read_size); + + if (!name.data) { + semsg(_(READERR("variable", "has wrong variable name type")), initial_fpos); goto shada_read_next_item_error; } - entry->data.global_var.name = - xmemdupz(unpacked.data.via.array.ptr[0].via.bin.ptr, - unpacked.data.via.array.ptr[0].via.bin.size); - SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, - entry->data.global_var.additional_elements, - "variable"); + entry->data.global_var.name = xmemdupz(name.data, name.size); + + String binval = unpack_string(&read_ptr, &read_size); + bool is_blob = false; - // A msgpack BIN could be a String or Blob; an additional VAR_TYPE_BLOB - // element is stored with Blobs which can be used to differentiate them - if (unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_BIN) { - const listitem_T *type_item - = tv_list_first(entry->data.global_var.additional_elements); - if (type_item != NULL) { - const typval_T *type_tv = TV_LIST_ITEM_TV(type_item); - if (type_tv->v_type != VAR_NUMBER - || type_tv->vval.v_number != VAR_TYPE_BLOB) { + if (binval.data) { + if (len > 2) { + // A msgpack BIN could be a String or Blob; an additional VAR_TYPE_BLOB + // element is stored with Blobs which can be used to differentiate them + Integer type; + if (!unpack_integer(&read_ptr, &read_size, &type) || type != VAR_TYPE_BLOB) { semsg(_(READERR("variable", "has wrong variable type")), initial_fpos); goto shada_read_next_item_error; } is_blob = true; } + entry->data.global_var.value = decode_string(binval.data, binval.size, is_blob, false); + } else { + int status = unpack_typval(&read_ptr, &read_size, &entry->data.global_var.value); + if (status != MPACK_OK) { + semsg(_(READERR("variable", "has value that cannot " + "be converted to the Vimscript value")), initial_fpos); + goto shada_read_next_item_error; + } } - if (is_blob) { - const msgpack_object_bin *const bin - = &unpacked.data.via.array.ptr[1].via.bin; - blob_T *const blob = tv_blob_alloc(); - ga_concat_len(&blob->bv_ga, bin->ptr, (size_t)bin->size); - tv_blob_set_ret(&entry->data.global_var.value, blob); - } else if (msgpack_to_vim(unpacked.data.via.array.ptr[1], - &(entry->data.global_var.value)) == FAIL) { - semsg(_(READERR("variable", "has value that cannot " - "be converted to the Vimscript value")), initial_fpos); - goto shada_read_next_item_error; - } + read_additional_array_elements = (uint32_t)(len - 2 - (is_blob ? 1 : 0)); break; } - case kSDItemSubString: - if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - semsg(_(READERR("sub string", "is not an array")), initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.size < 1) { - semsg(_(READERR("sub string", "does not have enough elements")), - initial_fpos); + case kSDItemSubString: { + ssize_t len = unpack_array(&read_ptr, &read_size); + + if (len < 1) { + semsg(_(READERR("sub string", "is not an array with enough elements")), initial_fpos); goto shada_read_next_item_error; } - if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { - semsg(_(READERR("sub string", "has wrong sub string type")), - initial_fpos); + + String sub = unpack_string(&read_ptr, &read_size); + if (!sub.data) { + semsg(_(READERR("sub string", "has wrong sub string type")), initial_fpos); goto shada_read_next_item_error; } - entry->data.sub_string.sub = - BIN_CONVERTED(unpacked.data.via.array.ptr[0].via.bin); - SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 1, - entry->data.sub_string.additional_elements, - "sub string"); + entry->data.sub_string.sub = xmemdupz(sub.data, sub.size); + read_additional_array_elements = (uint32_t)(len - 1); break; - case kSDItemBufferList: - if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { + } + case kSDItemBufferList: { + ssize_t len = unpack_array(&read_ptr, &read_size); + if (len < 0) { semsg(_(READERR("buffer list", "is not an array")), initial_fpos); goto shada_read_next_item_error; } - if (unpacked.data.via.array.size == 0) { + if (len == 0) { break; } - entry->data.buffer_list.buffers = - xcalloc(unpacked.data.via.array.size, - sizeof(*entry->data.buffer_list.buffers)); - for (size_t i = 0; i < unpacked.data.via.array.size; i++) { + entry->data.buffer_list.buffers = xcalloc((size_t)len, + sizeof(*entry->data.buffer_list.buffers)); + for (size_t i = 0; i < (size_t)len; i++) { entry->data.buffer_list.size++; - msgpack_unpacked unpacked_2 = (msgpack_unpacked) { - .data = unpacked.data.via.array.ptr[i], - }; - { - if (unpacked_2.data.type != MSGPACK_OBJECT_MAP) { - semsg(_(RERR "Error while reading ShaDa file: " - "buffer list at position %" PRIu64 " " - "contains entry that is not a dictionary"), - initial_fpos); - goto shada_read_next_item_error; - } - entry->data.buffer_list.buffers[i].pos = default_pos; - garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked_2.data.via.map.ptr)), 1); - { - // XXX: Temporarily reassign `i` because the macros depend on it. - const size_t j = i; - { - for (i = 0; i < unpacked_2.data.via.map.size; i++) { - CHECK_KEY_IS_STR(unpacked_2, "buffer list entry") - INTEGER_KEY(unpacked_2, "buffer list entry", KEY_LNUM, - entry->data.buffer_list.buffers[j].pos.lnum) - INTEGER_KEY(unpacked_2, "buffer list entry", KEY_COL, - entry->data.buffer_list.buffers[j].pos.col) - STRING_KEY(unpacked_2, "buffer list entry", KEY_FILE, - entry->data.buffer_list.buffers[j].fname) - ADDITIONAL_KEY(unpacked_2) - } - } - i = j; // XXX: Restore `i`. - } - if (entry->data.buffer_list.buffers[i].pos.lnum <= 0) { - semsg(_(RERR "Error while reading ShaDa file: " - "buffer list at position %" PRIu64 " " - "contains entry with invalid line number"), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - if (entry->data.buffer_list.buffers[i].pos.col < 0) { - semsg(_(RERR "Error while reading ShaDa file: " - "buffer list at position %" PRIu64 " " - "contains entry with invalid column number"), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - if (entry->data.buffer_list.buffers[i].fname == NULL) { - semsg(_(RERR "Error while reading ShaDa file: " - "buffer list at position %" PRIu64 " " - "contains entry that does not have a file name"), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - SET_ADDITIONAL_DATA(entry->data.buffer_list.buffers[i].additional_data, - "buffer list entry"); + Dict(_shada_buflist_item) it = { 0 }; + AdditionalDataBuilder it_ad = KV_INITIAL_VALUE; + if (!unpack_keydict(&it, DictHash(_shada_buflist_item), &it_ad, &read_ptr, &read_size, + &error_alloc)) { + semsg(_(RERR "Error while reading ShaDa file: " + "buffer list at position %" PRIu64 " contains entry that %s"), + initial_fpos, error_alloc); + kv_destroy(it_ad); + goto shada_read_next_item_error; + } + struct buffer_list_buffer *e = &entry->data.buffer_list.buffers[i]; + e->additional_data = (AdditionalData *)it_ad.items; + e->pos = default_pos; + if (HAS_KEY(&it, _shada_buflist_item, l)) { + e->pos.lnum = (linenr_T)it.l; + } + if (HAS_KEY(&it, _shada_buflist_item, c)) { + e->pos.col = (colnr_T)it.c; + } + if (HAS_KEY(&it, _shada_buflist_item, f)) { + e->fname = xmemdupz(it.f.data, it.f.size); + } + + if (e->pos.lnum <= 0) { + semsg(_(RERR "Error while reading ShaDa file: " + "buffer list at position %" PRIu64 " " + "contains entry with invalid line number"), + initial_fpos); + goto shada_read_next_item_error; + } + if (e->pos.col < 0) { + semsg(_(RERR "Error while reading ShaDa file: " + "buffer list at position %" PRIu64 " " + "contains entry with invalid column number"), + initial_fpos); + goto shada_read_next_item_error; + } + if (e->fname == NULL) { + semsg(_(RERR "Error while reading ShaDa file: " + "buffer list at position %" PRIu64 " " + "contains entry that does not have a file name"), + initial_fpos); + goto shada_read_next_item_error; } } break; + } case kSDItemMissing: case kSDItemUnknown: abort(); } + + for (uint32_t i = 0; i < read_additional_array_elements; i++) { + const char *item_start = read_ptr; + int status = unpack_skip(&read_ptr, &read_size); + if (status) { + goto shada_read_next_item_error; + } + + push_additional_data(&ad, item_start, (size_t)(read_ptr - item_start)); + } + + if (read_size) { + semsg(_(READERR("item", "additional bytes")), initial_fpos); + goto shada_read_next_item_error; + } + entry->type = (ShadaEntryType)type_u64; + entry->additional_data = (AdditionalData *)ad.items; ret = kSDReadStatusSuccess; shada_read_next_item_end: - if (buf != NULL) { - msgpack_unpacked_destroy(&unpacked); + if (buf_allocated) { xfree(buf); } return ret; @@ -3955,26 +3525,10 @@ shada_read_next_item_error: entry->type = (ShadaEntryType)type_u64; shada_free_shada_entry(entry); entry->type = kSDItemMissing; + xfree(error_alloc); + kv_destroy(ad); goto shada_read_next_item_end; } -#undef BIN_CONVERTED -#undef CHECK_KEY -#undef BOOLEAN_KEY -#undef CONVERTED_STRING_KEY -#undef STRING_KEY -#undef ADDITIONAL_KEY -#undef ID -#undef BINDUP -#undef TOCHAR -#undef TOINT -#undef TYPED_KEY -#undef INT_KEY -#undef INTEGER_KEY -#undef TOU8 -#undef TOSIZE -#undef SET_ADDITIONAL_DATA -#undef SET_ADDITIONAL_ELEMENTS -#undef CLEAR_GA_AND_ERROR_OUT /// Check whether "name" is on removable media (according to 'shada') /// @@ -4051,9 +3605,9 @@ static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps, .name = NUL, .mark = fm.fmark.mark, .fname = (char *)fname, - .additional_data = fm.fmark.additional_data, } - } + }, + .additional_data = fm.fmark.additional_data, } }; } while (jump_iter != NULL); @@ -4063,13 +3617,12 @@ static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps, /// Write registers ShaDa entries in given msgpack_sbuffer. /// /// @param[in] sbuf target msgpack_sbuffer to write to. -void shada_encode_regs(msgpack_sbuffer *const sbuf) +String shada_encode_regs(void) FUNC_ATTR_NONNULL_ALL { WriteMergerState *const wms = xcalloc(1, sizeof(*wms)); shada_initialize_registers(wms, -1); - msgpack_packer packer; - msgpack_packer_init(&packer, sbuf, msgpack_sbuffer_write); + PackerBuffer packer = packer_string_buffer(); for (size_t i = 0; i < ARRAY_SIZE(wms->registers); i++) { if (wms->registers[i].data.type == kSDItemRegister) { if (kSDWriteFailed @@ -4079,52 +3632,53 @@ void shada_encode_regs(msgpack_sbuffer *const sbuf) } } xfree(wms); + return packer_take_string(&packer); } /// Write jumplist ShaDa entries in given msgpack_sbuffer. /// /// @param[in] sbuf target msgpack_sbuffer to write to. -void shada_encode_jumps(msgpack_sbuffer *const sbuf) +String shada_encode_jumps(void) FUNC_ATTR_NONNULL_ALL { Set(ptr_t) removable_bufs = SET_INIT; find_removable_bufs(&removable_bufs); PossiblyFreedShadaEntry jumps[JUMPLISTSIZE]; size_t jumps_size = shada_init_jumps(jumps, &removable_bufs); - msgpack_packer packer; - msgpack_packer_init(&packer, sbuf, msgpack_sbuffer_write); + PackerBuffer packer = packer_string_buffer(); for (size_t i = 0; i < jumps_size; i++) { if (kSDWriteFailed == shada_pack_pfreed_entry(&packer, jumps[i], 0)) { abort(); } } + return packer_take_string(&packer); } /// Write buffer list ShaDa entry in given msgpack_sbuffer. /// /// @param[in] sbuf target msgpack_sbuffer to write to. -void shada_encode_buflist(msgpack_sbuffer *const sbuf) +String shada_encode_buflist(void) FUNC_ATTR_NONNULL_ALL { Set(ptr_t) removable_bufs = SET_INIT; find_removable_bufs(&removable_bufs); ShadaEntry buflist_entry = shada_get_buflist(&removable_bufs); - msgpack_packer packer; - msgpack_packer_init(&packer, sbuf, msgpack_sbuffer_write); + + PackerBuffer packer = packer_string_buffer(); if (kSDWriteFailed == shada_pack_entry(&packer, buflist_entry, 0)) { abort(); } xfree(buflist_entry.data.buffer_list.buffers); + return packer_take_string(&packer); } /// Write global variables ShaDa entries in given msgpack_sbuffer. /// /// @param[in] sbuf target msgpack_sbuffer to write to. -void shada_encode_gvars(msgpack_sbuffer *const sbuf) +String shada_encode_gvars(void) FUNC_ATTR_NONNULL_ALL { - msgpack_packer packer; - msgpack_packer_init(&packer, sbuf, msgpack_sbuffer_write); + PackerBuffer packer = packer_string_buffer(); const void *var_iter = NULL; const Timestamp cur_timestamp = os_time(); do { @@ -4145,9 +3699,9 @@ void shada_encode_gvars(msgpack_sbuffer *const sbuf) .global_var = { .name = (char *)name, .value = tgttv, - .additional_elements = NULL, } - } + }, + .additional_data = NULL, }, 0); if (kSDWriteFailed == r) { abort(); @@ -4156,77 +3710,21 @@ void shada_encode_gvars(msgpack_sbuffer *const sbuf) } tv_clear(&vartv); } while (var_iter != NULL); + return packer_take_string(&packer); } -/// Wrapper for reading from msgpack_sbuffer. -/// -/// @return number of bytes read. -static ptrdiff_t read_sbuf(ShaDaReadDef *const sd_reader, void *const dest, const size_t size) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - msgpack_sbuffer *sbuf = (msgpack_sbuffer *)sd_reader->cookie; - const uintmax_t bytes_read = MIN(size, sbuf->size - sd_reader->fpos); - if (bytes_read < size) { - sd_reader->eof = true; - } - memcpy(dest, sbuf->data + sd_reader->fpos, (size_t)bytes_read); - sd_reader->fpos += bytes_read; - return (ptrdiff_t)bytes_read; -} - -/// Wrapper for read that ignores bytes read from msgpack_sbuffer. -/// -/// Used for skipping. -/// -/// @param[in,out] sd_reader ShaDaReadDef with msgpack_sbuffer. -/// @param[in] offset Amount of bytes to skip. -/// -/// @return FAIL in case of failure, OK in case of success. May set -/// sd_reader->eof. -static int sd_sbuf_reader_skip_read(ShaDaReadDef *const sd_reader, const size_t offset) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - msgpack_sbuffer *sbuf = (msgpack_sbuffer *)sd_reader->cookie; - assert(sbuf->size >= sd_reader->fpos); - const uintmax_t skip_bytes = MIN(offset, sbuf->size - sd_reader->fpos); - if (skip_bytes < offset) { - sd_reader->eof = true; - return FAIL; - } - sd_reader->fpos += offset; - return OK; -} - -/// Prepare ShaDaReadDef with msgpack_sbuffer for reading. -/// -/// @param[in] sbuf msgpack_sbuffer to read from. -/// @param[out] sd_reader Location where reader structure will be saved. -static void open_shada_sbuf_for_reading(const msgpack_sbuffer *const sbuf, ShaDaReadDef *sd_reader) - FUNC_ATTR_NONNULL_ALL -{ - *sd_reader = (ShaDaReadDef) { - .read = &read_sbuf, - .close = NULL, - .skip = &sd_sbuf_reader_skip_read, - .error = NULL, - .eof = false, - .fpos = 0, - .cookie = (void *)sbuf, - }; -} - -/// Read ShaDa from msgpack_sbuffer. +/// Read ShaDa from String. /// -/// @param[in] file msgpack_sbuffer to read from. +/// @param[in] string string to read from. /// @param[in] flags Flags, see ShaDaReadFileFlags enum. -void shada_read_sbuf(msgpack_sbuffer *const sbuf, const int flags) +void shada_read_string(String string, const int flags) FUNC_ATTR_NONNULL_ALL { - assert(sbuf != NULL); - if (sbuf->data == NULL) { + if (string.size == 0) { return; } - ShaDaReadDef sd_reader; - open_shada_sbuf_for_reading(sbuf, &sd_reader); + FileDescriptor sd_reader; + file_open_buffer(&sd_reader, string.data, string.size); shada_read(&sd_reader, flags); + close_file(&sd_reader); } |