diff options
author | ZyX <kp-pav@yandex.ru> | 2015-07-19 21:23:15 +0300 |
---|---|---|
committer | ZyX <kp-pav@yandex.ru> | 2015-10-08 22:00:13 +0300 |
commit | 43fe98c9fb350b428d05021995c8892e080054b2 (patch) | |
tree | beb6614b0c334de02f40ce5555eac57b691a2d93 /src | |
parent | 4bc053facda4aee6cec2c6cbc9fbbe978e66503d (diff) | |
download | rneovim-43fe98c9fb350b428d05021995c8892e080054b2.tar.gz rneovim-43fe98c9fb350b428d05021995c8892e080054b2.tar.bz2 rneovim-43fe98c9fb350b428d05021995c8892e080054b2.zip |
shada: Add support for merging everything like described in the doc
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/lib/khash.h | 20 | ||||
-rw-r--r-- | src/nvim/mark.c | 8 | ||||
-rw-r--r-- | src/nvim/mark.h | 28 | ||||
-rw-r--r-- | src/nvim/mark_defs.h | 6 | ||||
-rw-r--r-- | src/nvim/ops.c | 45 | ||||
-rw-r--r-- | src/nvim/ops.h | 43 | ||||
-rw-r--r-- | src/nvim/shada.c | 1141 |
7 files changed, 862 insertions, 429 deletions
diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h index fd4f910dac..17f653f45e 100644 --- a/src/nvim/lib/khash.h +++ b/src/nvim/lib/khash.h @@ -196,6 +196,7 @@ static const double __ac_HASH_UPPER = 0.77; #define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ extern kh_##name##_t *kh_init_##name(void); \ + extern void kh_dealloc_##name(kh_##name##_t *h); \ extern void kh_destroy_##name(kh_##name##_t *h); \ extern void kh_clear_##name(kh_##name##_t *h); \ extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ @@ -204,16 +205,24 @@ static const double __ac_HASH_UPPER = 0.77; extern void kh_del_##name(kh_##name##_t *h, khint_t x); #define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + SCOPE kh_##name##_t *kh_init_##name(void) \ + REAL_FATTR_UNUSED; \ SCOPE kh_##name##_t *kh_init_##name(void) { \ return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ } \ + SCOPE void kh_dealloc_##name(kh_##name##_t *h) \ + REAL_FATTR_UNUSED; \ + SCOPE void kh_dealloc_##name(kh_##name##_t *h) \ + { \ + kfree((void *)h->keys); kfree(h->flags); \ + kfree((void *)h->vals); \ + } \ SCOPE void kh_destroy_##name(kh_##name##_t *h) \ REAL_FATTR_UNUSED; \ SCOPE void kh_destroy_##name(kh_##name##_t *h) \ { \ if (h) { \ - kfree((void *)h->keys); kfree(h->flags); \ - kfree((void *)h->vals); \ + kh_dealloc_##name(h); \ kfree(h); \ } \ } \ @@ -447,6 +456,13 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key) #define kh_destroy(name, h) kh_destroy_##name(h) /*! @function + @abstract Free memory referenced directly inside a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_dealloc(name, h) kh_dealloc_##name(h) + +/*! @function @abstract Reset a hash table without deallocating memory. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] diff --git a/src/nvim/mark.c b/src/nvim/mark.c index b7746f192f..2072483e1d 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -1350,12 +1350,8 @@ size_t mark_buffer_amount(const buf_T *const buf) /// later then existing one. void mark_set_global(const char name, const xfmark_T fm, const bool update) { - xfmark_T *fm_tgt = NULL; - if (ASCII_ISUPPER(name)) { - fm_tgt = &(namedfm[name - 'A']); - } else if (ascii_isdigit(name)) { - fm_tgt = &(namedfm[NMARKS + (name - '0')]); - } else { + xfmark_T *fm_tgt = &(namedfm[mark_global_index(name)]); + if (fm_tgt == &namedfm[-1]) { return; } if (update && fm.fmark.timestamp < fm_tgt->fmark.timestamp) { diff --git a/src/nvim/mark.h b/src/nvim/mark.h index 3f63e274bb..aff6e7273a 100644 --- a/src/nvim/mark.h +++ b/src/nvim/mark.h @@ -1,6 +1,8 @@ #ifndef NVIM_MARK_H #define NVIM_MARK_H +#include "nvim/macros.h" +#include "nvim/ascii.h" #include "nvim/buffer_defs.h" #include "nvim/mark_defs.h" #include "nvim/memory.h" @@ -46,6 +48,32 @@ SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_); \ } while (0) +/// Convert mark name to the offset +static inline int mark_global_index(const char name) + FUNC_ATTR_CONST +{ + return (ASCII_ISUPPER(name) + ? (name - 'A') + : (ascii_isdigit(name) + ? (NMARKS + (name - '0')) + : -1)); +} + +/// Convert local mark name to the offset +static inline int mark_local_index(const char name) + FUNC_ATTR_CONST +{ + return (ASCII_ISLOWER(name) + ? (name - 'a') + : (name == '"' + ? NMARKS + : (name == '^' + ? NMARKS + 1 + : (name == '.' + ? NMARKS + 2 + : -1)))); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mark.h.generated.h" #endif diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h index 2f7c9b4ed4..af4727d634 100644 --- a/src/nvim/mark_defs.h +++ b/src/nvim/mark_defs.h @@ -19,6 +19,12 @@ /// Total possible number of global marks #define NGLOBALMARKS (NMARKS + EXTRA_MARKS) +/// Total possible number of local marks +/// +/// That are uppercase marks plus '"', '^' and '.'. There are other local marks, +/// but they are not saved in ShaDa files. +#define NLOCALMARKS (NMARKS + 3) + /// Maximum number of marks in jump list #define JUMPLISTSIZE 100 diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 52064fae06..0d7c319fba 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -54,22 +54,6 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -/* - * Registers: - * 0 = register for latest (unnamed) yank - * 1..9 = registers '1' to '9', for deletes - * 10..35 = registers 'a' to 'z' - * 36 = delete register '-' - * 37 = selection register '*' - * 38 = clipboard register '+' - */ -#define DELETION_REGISTER 36 -#define NUM_SAVED_REGISTERS 37 -// The following registers should not be saved in ShaDa file: -#define STAR_REGISTER 37 -#define PLUS_REGISTER 38 -#define NUM_REGISTERS 39 - static yankreg_T y_regs[NUM_REGISTERS]; static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */ @@ -749,31 +733,6 @@ typedef enum { YREG_PUT, } yreg_mode_t; -/// Convert register name into register index -/// -/// @param[in] regname Register name. -/// -/// @return Index in y_regs array or -1 if register name was not recognized. -static inline int reg_index(const int regname) - FUNC_ATTR_CONST -{ - if (ascii_isdigit(regname)) { - return regname - '0'; - } else if (ASCII_ISLOWER(regname)) { - return CharOrdLow(regname) + 10; - } else if (ASCII_ISUPPER(regname)) { - return CharOrdUp(regname) + 10; - } else if (regname == '-') { - return DELETION_REGISTER; - } else if (regname == '*') { - return STAR_REGISTER; - } else if (regname == '+') { - return PLUS_REGISTER; - } else { - return -1; - } -} - /// Return yankreg_T to use, according to the value of `regname`. /// Cannot handle the '_' (black hole) register. /// Must only be called with a valid register name! @@ -806,7 +765,7 @@ yankreg_T *get_yank_register(int regname, int mode) return y_previous; } - int i = reg_index(regname); + int i = op_reg_index(regname); // when not 0-9, a-z, A-Z or '-'/'+'/'*': use register 0 if (i == -1) { i = 0; @@ -5417,7 +5376,7 @@ size_t op_register_amount(void) /// Set register to a given value void register_set(const char name, const yankreg_T reg) { - int i = reg_index(name); + int i = op_reg_index(name); if (i == -1) { return; } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 4da5cfc93d..5565f1631f 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -3,6 +3,8 @@ #include <stdbool.h> +#include "nvim/macros.h" +#include "nvim/ascii.h" #include "nvim/types.h" #include "nvim/api/private/defs.h" #include "nvim/os/time.h" @@ -18,6 +20,22 @@ typedef int (*Indenter)(void); #define PUT_LINE_FORWARD 32 /* put linewise register below Visual sel. */ /* + * Registers: + * 0 = register for latest (unnamed) yank + * 1..9 = registers '1' to '9', for deletes + * 10..35 = registers 'a' to 'z' + * 36 = delete register '-' + * 37 = selection register '*' + * 38 = clipboard register '+' + */ +#define DELETION_REGISTER 36 +#define NUM_SAVED_REGISTERS 37 +// The following registers should not be saved in ShaDa file: +#define STAR_REGISTER 37 +#define PLUS_REGISTER 38 +#define NUM_REGISTERS 39 + +/* * Operator IDs; The order must correspond to opchars[] in ops.c! */ #define OP_NOP 0 /* no pending operation */ @@ -66,6 +84,31 @@ typedef struct yankreg { Dictionary *additional_data; ///< Additional data from ShaDa file. } yankreg_T; +/// Convert register name into register index +/// +/// @param[in] regname Register name. +/// +/// @return Index in y_regs array or -1 if register name was not recognized. +static inline int op_reg_index(const int regname) + FUNC_ATTR_CONST +{ + if (ascii_isdigit(regname)) { + return regname - '0'; + } else if (ASCII_ISLOWER(regname)) { + return CharOrdLow(regname) + 10; + } else if (ASCII_ISUPPER(regname)) { + return CharOrdUp(regname) + 10; + } else if (regname == '-') { + return DELETION_REGISTER; + } else if (regname == '*') { + return STAR_REGISTER; + } else if (regname == '+') { + return PLUS_REGISTER; + } else { + return -1; + } +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ops.h.generated.h" #endif diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 6920115735..a7bde92032 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1,3 +1,4 @@ +#include <stdlib.h> #include <stddef.h> #include <stdbool.h> #include <string.h> @@ -154,7 +155,6 @@ enum SRNIFlags { kSDReadUndisableableData = ( (1 << kSDItemSearchPattern) | (1 << kSDItemSubString) - | (1 << kSDItemGlobalMark) | (1 << kSDItemJump) ), ///< Data reading which cannot be disabled by &shada or other options ///< except for disabling reading ShaDa as a whole. @@ -175,6 +175,11 @@ enum SRNIFlags { kSDReadUnknown = (1 << (SHADA_LAST_ENTRY + 1)), ///< Determines whether ///< unknown items should be ///< read (usually disabled). + kSDReadGlobalMarks = (1 << kSDItemGlobalMark), ///< Determines whether global + ///< marks should be read. Can + ///< only be disabled by + ///< having f0 in &shada when + ///< writing. kSDReadLocalMarks = (1 << kSDItemLocalMark), ///< Determines whether local ///< marks should be read. Can ///< only be disabled by @@ -286,6 +291,41 @@ typedef struct { uint8_t history_type; } HistoryMergerState; +/// ShadaEntry structure that knows whether it should be freed +typedef struct { + ShadaEntry data; ///< ShadaEntry data. + bool can_free_entry; ///< True if entry can be freed. +} PossiblyFreedShadaEntry; + +/// Structure that holds one file marks. +typedef struct { + PossiblyFreedShadaEntry marks[NLOCALMARKS]; ///< All file marks. + PossiblyFreedShadaEntry changes[JUMPLISTSIZE]; ///< All file changes. + size_t changes_size; ///< Number of changes occupied. + ShadaEntry *additional_marks; ///< All marks with unknown names. + size_t additional_marks_size; ///< Size of the additional_marks array. + Timestamp greatest_timestamp; ///< Greatest timestamp among marks. + bool is_local_entry; ///< True if structure comes from the current session. +} FileMarks; + +KHASH_MAP_INIT_STR(file_marks, FileMarks) + +/// State structure used by shada_write +/// +/// Before actually writing most of the data is read to this structure. +typedef struct { + HistoryMergerState hms[HIST_COUNT]; ///< Structures for history merging. + PossiblyFreedShadaEntry global_marks[NGLOBALMARKS]; ///< All global marks. + PossiblyFreedShadaEntry registers[NUM_SAVED_REGISTERS]; ///< All registers. + PossiblyFreedShadaEntry jumps[JUMPLISTSIZE]; ///< All dumped jumps. + size_t jumps_size; ///< Number of jumps occupied. + PossiblyFreedShadaEntry search_pattern; ///< Last search pattern. + PossiblyFreedShadaEntry sub_search_pattern; ///< Last s/ search pattern. + PossiblyFreedShadaEntry replacement; ///< Last s// replacement string. + khash_t(strset) dumped_variables; ///< Names of already dumped variables. + khash_t(file_marks) file_marks; ///< All file marks. +} WriteMergerState; + struct sd_read_def; /// Function used to read ShaDa files @@ -457,18 +497,6 @@ static inline void hmll_dealloc(HMLList *const hmll) xfree(hmll->free_entries); } -/// Free linked list and all entries -/// -/// @param[in] hmll List to free. -static inline void hmll_free(HMLList *const hmll) - FUNC_ATTR_NONNULL_ALL -{ - HMLL_FORALL(hmll, cur_entry) { - shada_free_shada_entry(&cur_entry->data); - } - hmll_dealloc(hmll); -} - /// Wrapper for reading from file descriptors /// /// @return true if read was successfull, false otherwise. @@ -897,14 +925,7 @@ static inline void hms_to_he_array(const HistoryMergerState *const hms_p, static inline void hms_dealloc(HistoryMergerState *const hms_p) FUNC_ATTR_NONNULL_ALL { - if (hms_p->reading) { - // Free only the linked list if reading because all of the allocated memory - // was either already freed or saved in internal NeoVim history. - hmll_dealloc(&hms_p->hmll); - } else { - // Free everything because when writing data is only used once to write it. - hmll_free(&hms_p->hmll); - } + hmll_dealloc(&hms_p->hmll); } /// Iterate over all history entries in history merger, in order @@ -957,30 +978,28 @@ static inline bool marks_equal(const pos_T a, const pos_T b) static void shada_read(ShaDaReadDef *const sd_reader, const int flags) FUNC_ATTR_NONNULL_ALL { - unsigned srni_flags = 0; const bool force = flags & kShaDaForceit; const bool get_old_files = flags & (kShaDaGetOldfiles | kShaDaForceit); const bool want_marks = flags & kShaDaWantMarks; - if (flags & kShaDaWantInfo) { - srni_flags |= kSDReadUndisableableData | kSDReadRegisters; - if (p_hi) { - srni_flags |= kSDReadHistory; - } - if (find_shada_parameter('!') != NULL) { - srni_flags |= kSDReadVariables; - } - if (find_shada_parameter('%') != NULL && ARGCOUNT == 0) { - srni_flags |= kSDReadBufferList; - } - } - if (want_marks) { - if (get_shada_parameter('\'') > 0) { - srni_flags |= kSDReadLocalMarks | kSDReadChanges; - } - } - if (get_old_files) { - srni_flags |= kSDReadLocalMarks; - } + const unsigned srni_flags = ((flags & kShaDaWantInfo + ? (kSDReadUndisableableData + | kSDReadRegisters + | kSDReadGlobalMarks + | (p_hi ? kSDReadHistory : 0) + | (find_shada_parameter('!') != NULL + ? kSDReadVariables + : 0) + | (find_shada_parameter('%') != NULL + && ARGCOUNT == 0 + ? kSDReadBufferList + : 0)) + : 0) + | (want_marks && get_shada_parameter('\'') > 0 + ? kSDReadLocalMarks | kSDReadChanges + : 0) + | (get_old_files + ? kSDReadLocalMarks + : 0)); if (srni_flags == 0) { // Nothing to do. return; @@ -1009,7 +1028,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) oldfiles_list = list_alloc(); set_vim_var_list(VV_OLDFILES, oldfiles_list); } - while (shada_read_next_item(sd_reader, &cur_entry, srni_flags) == NOTDONE) { + while (shada_read_next_item(sd_reader, &cur_entry, srni_flags, 0) + == NOTDONE) { switch (cur_entry.type) { case kSDItemMissing: { assert(false); @@ -1689,6 +1709,140 @@ static void shada_pack_entry(msgpack_packer *const packer, msgpack_sbuffer_destroy(&sbuf); } +/// Write single ShaDa entry, converting it if needed +/// +/// @warning Frees entry after packing. +/// +/// @param[in] packer Packer used to write entry. +/// @param[in] sd_conv Conversion definitions. +/// @param[in] entry Entry written. If entry.can_free_entry is false then +/// it assumes that entry was not converted, otherwise it +/// is assumed that entry was already converted. +/// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no +/// restrictions. +static void shada_pack_encoded_entry(msgpack_packer *const packer, + const vimconv_T *const sd_conv, + PossiblyFreedShadaEntry entry, + const size_t max_kbyte) + FUNC_ATTR_NONNULL_ALL +{ + if (entry.can_free_entry) { + shada_pack_entry(packer, entry.data, max_kbyte); + shada_free_shada_entry(&entry.data); + return; + } +#define RUN_WITH_CONVERTED_STRING(cstr, code) \ + do { \ + bool did_convert = false; \ + if (sd_conv->vc_type != CONV_NONE && has_non_ascii((cstr))) { \ + char *const converted_string = string_convert(sd_conv, (cstr), NULL); \ + if (converted_string != NULL) { \ + (cstr) = converted_string; \ + did_convert = true; \ + } \ + } \ + code \ + if (did_convert) { \ + xfree((cstr)); \ + } \ + } while (0) + switch (entry.data.type) { + case kSDItemUnknown: + case kSDItemMissing: { + assert(false); + } + case kSDItemSearchPattern: { + RUN_WITH_CONVERTED_STRING(entry.data.data.search_pattern.pat, { + shada_pack_entry(packer, entry.data, max_kbyte); + }); + break; + } + case kSDItemHistoryEntry: { + RUN_WITH_CONVERTED_STRING(entry.data.data.history_item.string, { + shada_pack_entry(packer, entry.data, max_kbyte); + }); + break; + } + case kSDItemSubString: { + RUN_WITH_CONVERTED_STRING(entry.data.data.sub_string.sub, { + shada_pack_entry(packer, entry.data, max_kbyte); + }); + break; + } + case kSDItemVariable: { + if (sd_conv->vc_type != CONV_NONE) { + convert_object(sd_conv, &entry.data.data.global_var.value); + } + shada_pack_entry(packer, entry.data, max_kbyte); + break; + } + case kSDItemRegister: { + bool did_convert = false; + if (sd_conv->vc_type != CONV_NONE) { + size_t first_non_ascii = 0; + for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) { + if (has_non_ascii(entry.data.data.reg.contents[i])) { + first_non_ascii = i; + did_convert = true; + break; + } + } + if (did_convert) { + entry.data.data.reg.contents = + xmemdup(entry.data.data.reg.contents, + (entry.data.data.reg.contents_size + * sizeof(entry.data.data.reg.contents))); + for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) { + if (i >= first_non_ascii) { + entry.data.data.reg.contents[i] = get_converted_string( + sd_conv, + entry.data.data.reg.contents[i], + strlen(entry.data.data.reg.contents[i])); + } else { + entry.data.data.reg.contents[i] = + xstrdup(entry.data.data.reg.contents[i]); + } + } + } + } + shada_pack_entry(packer, entry.data, max_kbyte); + if (did_convert) { + for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) { + xfree(entry.data.data.reg.contents[i]); + } + xfree(entry.data.data.reg.contents); + } + break; + } + case kSDItemHeader: + case kSDItemGlobalMark: + case kSDItemJump: + case kSDItemBufferList: + case kSDItemLocalMark: + case kSDItemChange: { + shada_pack_entry(packer, entry.data, max_kbyte); + break; + } + } +#undef RUN_WITH_CONVERTED_STRING +} + +/// Compare two FileMarks structure to order them by greatest_timestamp +/// +/// Order is reversed: structure with greatest greatest_timestamp comes first. +/// Function signature is compatible with qsort. +static int compare_file_marks(const void *a, const void *b) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + const FileMarks *const *const a_fms = a; + const FileMarks *const *const b_fms = b; + return ((*a_fms)->greatest_timestamp == (*b_fms)->greatest_timestamp + ? 0 + : ((*a_fms)->greatest_timestamp > (*b_fms)->greatest_timestamp + ? -1 + : 1)); +} + /// Write ShaDa file /// /// @param[in] sd_writer Structure containing file writer definition. @@ -1699,11 +1853,6 @@ static void shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef *const sd_reader) FUNC_ATTR_NONNULL_ARG(1) { - // TODO(ZyX-I): Write only one search pattern, substitute search pattern and - // substitute replacement string, which has the greatest timestamp. Make sure - // that this also applies if ShaDa file contains more then one replacement - // string. - khash_t(bufset) *const removable_bufs = kh_init(bufset); int max_kbyte_i = get_shada_parameter('s'); if (max_kbyte_i < 0) { max_kbyte_i = 10; @@ -1711,10 +1860,49 @@ static void shada_write(ShaDaWriteDef *const sd_writer, if (max_kbyte_i == 0) { return; } + + WriteMergerState *const wms = xcalloc(1, sizeof(*wms)); + bool dump_one_history[HIST_COUNT]; + const bool dump_global_vars = (find_shada_parameter('!') != NULL); + int max_reg_lines = get_shada_parameter('<'); + if (max_reg_lines < 0) { + max_reg_lines = get_shada_parameter('"'); + } + const bool limit_reg_lines = max_reg_lines >= 0; + const bool dump_registers = (max_reg_lines != 0); + khash_t(bufset) *const removable_bufs = kh_init(bufset); const size_t max_kbyte = (size_t) max_kbyte_i; + const size_t num_marked_files = (size_t) get_shada_parameter('\''); + const bool dump_global_marks = get_shada_parameter('f') != 0; + bool dump_history = false; - msgpack_packer *packer = msgpack_packer_new(sd_writer, - &msgpack_sd_writer_write); + // Initialize history merger + for (uint8_t i = 0; i < HIST_COUNT; i++) { + long num_saved = get_shada_parameter(hist_type2char(i)); + if (num_saved == -1) { + num_saved = p_hi; + } + if (num_saved > 0) { + dump_history = true; + dump_one_history[i] = true; + hms_init(&wms->hms[i], i, (size_t) num_saved, sd_reader != NULL, false); + } else { + dump_one_history[i] = false; + } + } + + const unsigned srni_flags = ( + kSDReadUndisableableData + | kSDReadUnknown + | (dump_history ? kSDReadHistory : 0) + | (dump_registers ? kSDReadRegisters : 0) + | (dump_global_vars ? kSDReadVariables : 0) + | (dump_global_marks ? kSDReadGlobalMarks : 0) + | (num_marked_files ? kSDReadLocalMarks | kSDReadChanges : 0) + ); + + msgpack_packer *const packer = msgpack_packer_new(sd_writer, + &msgpack_sd_writer_write); FOR_ALL_BUFFERS(buf) { if (buf->b_ffname != NULL && shada_removable((char *) buf->b_ffname)) { @@ -1723,10 +1911,7 @@ static void shada_write(ShaDaWriteDef *const sd_writer, } } - // TODO(ZyX-I): Iterate over sd_reader, keeping “replaced” values in a set. - - // First write values that do not require merging - // 1. Header + // Write header shada_pack_entry(packer, (ShadaEntry) { .type = kSDItemHeader, .timestamp = os_time(), @@ -1748,7 +1933,7 @@ static void shada_write(ShaDaWriteDef *const sd_writer, } }, 0); - // 2. Buffer list + // Write buffer list if (find_shada_parameter('%') != NULL) { size_t buf_count = 0; FOR_ALL_BUFFERS(buf) { @@ -1784,129 +1969,94 @@ static void shada_write(ShaDaWriteDef *const sd_writer, xfree(buflist_entry.data.buffer_list.buffers); } - // 3. Jump list - const void *jump_iter = NULL; - do { - xfmark_T fm; - cleanup_jumplist(); - jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm); - const buf_T *const buf = (fm.fmark.fnum == 0 - ? NULL - : buflist_findnr(fm.fmark.fnum)); - if (buf != NULL - ? SHADA_REMOVABLE(buf) - : fm.fmark.fnum != 0) { - continue; - } - const char *const fname = (char *) (fm.fmark.fnum == 0 - ? (fm.fname == NULL - ? NULL - : fm.fname) - : buf->b_ffname); - shada_pack_entry(packer, (ShadaEntry) { - .type = kSDItemJump, - .timestamp = fm.fmark.timestamp, - .data = { - .filemark = { - .name = NUL, - .mark = fm.fmark.mark, - .fname = (char *) fname, - .additional_data = fm.fmark.additional_data, - } + // Write some of the variables + if (dump_global_vars) { + const void *var_iter = NULL; + const Timestamp cur_timestamp = os_time(); + do { + typval_T vartv; + const char *name; + var_iter = var_shada_iter(var_iter, &name, &vartv); + if (var_iter == NULL && vartv.v_type == VAR_UNKNOWN) { + break; } - }, max_kbyte); - } while (jump_iter != NULL); - - // FIXME No merging currently - -#define RUN_WITH_CONVERTED_STRING(cstr, code) \ - do { \ - bool did_convert = false; \ - if (sd_writer->sd_conv.vc_type != CONV_NONE && has_non_ascii((cstr))) { \ - char *const converted_string = string_convert(&sd_writer->sd_conv, \ - (cstr), NULL); \ - if (converted_string != NULL) { \ - (cstr) = converted_string; \ - did_convert = true; \ - } \ - } \ - code \ - if (did_convert) { \ - xfree((cstr)); \ - } \ - } while (0) - // 4. History - HistoryMergerState hms[HIST_COUNT]; - for (uint8_t i = 0; i < HIST_COUNT; i++) { - long num_saved = get_shada_parameter(hist_type2char(i)); - if (num_saved == -1) { - num_saved = p_hi; - } - if (num_saved > 0) { - hms_init(&hms[i], i, (size_t) num_saved, false, false); - hms_insert_whole_neovim_history(&hms[i]); - HMS_ITER(&hms[i], cur_entry) { - RUN_WITH_CONVERTED_STRING(cur_entry->data.data.history_item.string, { - shada_pack_entry(packer, cur_entry->data, max_kbyte); - }); + Object obj = vim_to_object(&vartv); + if (sd_writer->sd_conv.vc_type != CONV_NONE) { + convert_object(&sd_writer->sd_conv, &obj); } - hms_dealloc(&hms[i]); - } + shada_pack_entry(packer, (ShadaEntry) { + .type = kSDItemVariable, + .timestamp = cur_timestamp, + .data = { + .global_var = { + .name = (char *) name, + .value = obj, + .additional_elements = NULL, + } + } + }, max_kbyte); + api_free_object(obj); + clear_tv(&vartv); + int kh_ret; + (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); + } while (var_iter != NULL); } - // 5. Search patterns + // Initialize search pattern { SearchPattern pat; get_search_pattern(&pat); - ShadaEntry sp_entry = (ShadaEntry) { - .type = kSDItemSearchPattern, - .timestamp = pat.timestamp, + wms->search_pattern = (PossiblyFreedShadaEntry) { + .can_free_entry = false, .data = { - .search_pattern = { - .magic = pat.magic, - .smartcase = !pat.no_scs, - .has_line_offset = pat.off.line, - .place_cursor_at_end = pat.off.end, - .offset = pat.off.off, - .is_last_used = search_was_last_used(), - .is_substitute_pattern = false, - .highlighted = (!no_hlsearch && find_shada_parameter('h') != NULL), - .pat = (char *) pat.pat, - .additional_data = pat.additional_data, + .type = kSDItemSearchPattern, + .timestamp = pat.timestamp, + .data = { + .search_pattern = { + .magic = pat.magic, + .smartcase = !pat.no_scs, + .has_line_offset = pat.off.line, + .place_cursor_at_end = pat.off.end, + .offset = pat.off.off, + .is_last_used = search_was_last_used(), + .is_substitute_pattern = false, + .highlighted = (!no_hlsearch && find_shada_parameter('h') != NULL), + .pat = (char *) pat.pat, + .additional_data = pat.additional_data, + } } } }; - RUN_WITH_CONVERTED_STRING(sp_entry.data.search_pattern.pat, { - shada_pack_entry(packer, sp_entry, max_kbyte); - }); } + + // Initialize substitute search pattern { SearchPattern pat; get_substitute_pattern(&pat); - ShadaEntry sp_entry = (ShadaEntry) { - .type = kSDItemSearchPattern, - .timestamp = pat.timestamp, + wms->sub_search_pattern = (PossiblyFreedShadaEntry) { + .can_free_entry = false, .data = { - .search_pattern = { - .magic = pat.magic, - .smartcase = !pat.no_scs, - .has_line_offset = false, - .place_cursor_at_end = false, - .offset = 0, - .is_last_used = !search_was_last_used(), - .is_substitute_pattern = true, - .highlighted = false, - .pat = (char *) pat.pat, - .additional_data = pat.additional_data, + .type = kSDItemSearchPattern, + .timestamp = pat.timestamp, + .data = { + .search_pattern = { + .magic = pat.magic, + .smartcase = !pat.no_scs, + .has_line_offset = false, + .place_cursor_at_end = false, + .offset = 0, + .is_last_used = !search_was_last_used(), + .is_substitute_pattern = true, + .highlighted = false, + .pat = (char *) pat.pat, + .additional_data = pat.additional_data, + } } } }; - RUN_WITH_CONVERTED_STRING(sp_entry.data.search_pattern.pat, { - shada_pack_entry(packer, sp_entry, max_kbyte); - }); } - // 6. Substitute string + // Initialize substitute replacement string { SubReplacementString sub; sub_get_replacement(&sub); @@ -1923,119 +2073,482 @@ static void shada_write(ShaDaWriteDef *const sd_writer, } } }; - RUN_WITH_CONVERTED_STRING(sub_entry.data.sub_string.sub, { - shada_pack_entry(packer, sub_entry, max_kbyte); - }); } - // 7. Global marks - if (get_shada_parameter('f') != 0) { - ShadaEntry *const global_marks = list_global_marks(removable_bufs); - for (ShadaEntry *mark = global_marks; mark->type != kSDItemMissing; - mark++) { - shada_pack_entry(packer, *mark, max_kbyte); - } - xfree(global_marks); - } - - // 8. Buffer marks and buffer change list - FOR_ALL_BUFFERS(buf) { - if (buf->b_ffname == NULL || SHADA_REMOVABLE(buf)) { + // Initialize jump list + const void *jump_iter = NULL; + do { + xfmark_T fm; + cleanup_jumplist(); + jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm); + const buf_T *const buf = (fm.fmark.fnum == 0 + ? NULL + : buflist_findnr(fm.fmark.fnum)); + if (buf != NULL + ? SHADA_REMOVABLE(buf) + : fm.fmark.fnum != 0) { continue; } - ShadaEntry *const buffer_marks = list_buffer_marks(buf); - for (ShadaEntry *mark = buffer_marks; mark->type != kSDItemMissing; - mark++) { - shada_pack_entry(packer, *mark, max_kbyte); + const char *const fname = (char *) (fm.fmark.fnum == 0 + ? (fm.fname == NULL + ? NULL + : fm.fname) + : buf->b_ffname); + if (fname == NULL) { + continue; } - xfree(buffer_marks); - - for (int i = 0; i < buf->b_changelistlen; i++) { - const fmark_T fm = buf->b_changelist[i]; - shada_pack_entry(packer, (ShadaEntry) { - .type = kSDItemChange, - .timestamp = fm.timestamp, + wms->jumps[wms->jumps_size++] = (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemJump, + .timestamp = fm.fmark.timestamp, .data = { .filemark = { - .mark = fm.mark, - .fname = (char *) buf->b_ffname, - .additional_data = fm.additional_data, + .name = NUL, + .mark = fm.fmark.mark, + .fname = (char *) fname, + .additional_data = fm.fmark.additional_data, } } - }, max_kbyte); - } - } - // FIXME: Copy previous marks, up to num_marked_files - // size_t num_marked_files = get_shada_parameter('\''); + } + }; + } while (jump_iter != NULL); - // 9. Registers - int max_num_lines_i = get_shada_parameter('<'); - if (max_num_lines_i < 0) { - max_num_lines_i = get_shada_parameter('"'); + // Initialize global marks + if (dump_global_marks) { + const void *global_mark_iter = NULL; + do { + char name; + xfmark_T fm; + global_mark_iter = mark_global_iter(global_mark_iter, &name, &fm); + if (fm.fmark.mark.lnum == 0) { + break; + } + const char *fname; + if (fm.fmark.fnum == 0) { + assert(fm.fname != NULL); + if (shada_removable((const char *) fm.fname)) { + continue; + } + fname = (const char *) fm.fname; + } else { + const buf_T *const buf = buflist_findnr(fm.fmark.fnum); + if (buf == NULL || buf->b_ffname == NULL || SHADA_REMOVABLE(buf)) { + continue; + } + fname = (const char *) buf->b_ffname; + } + wms->global_marks[mark_global_index(name)] = (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemGlobalMark, + .timestamp = fm.fmark.timestamp, + .data = { + .filemark = { + .mark = fm.fmark.mark, + .name = name, + .additional_data = fm.fmark.additional_data, + .fname = (char *) fname, + } + } + }, + }; + } while(global_mark_iter != NULL); } - if (max_num_lines_i != 0) { - const size_t max_num_lines = (max_num_lines_i < 0 - ? 0 - : (size_t) max_num_lines_i); - ShadaEntry *const registers = list_registers(max_num_lines); - for (ShadaEntry *reg = registers; reg->type != kSDItemMissing; reg++) { - bool did_convert = false; - if (sd_writer->sd_conv.vc_type != CONV_NONE) { - did_convert = true; - reg->data.reg.contents = xmemdup(reg->data.reg.contents, - (reg->data.reg.contents_size - * sizeof(reg->data.reg.contents))); - for (size_t i = 0; i < reg->data.reg.contents_size; i++) { - reg->data.reg.contents[i] = get_converted_string( - &sd_writer->sd_conv, - reg->data.reg.contents[i], strlen(reg->data.reg.contents[i])); + + // Initialize registers + if (dump_registers) { + const void *reg_iter = NULL; + do { + yankreg_T reg; + char name; + reg_iter = op_register_iter(reg_iter, &name, ®); + if (reg.y_array == NULL) { + break; + } + if (limit_reg_lines && reg.y_size > max_reg_lines) { + continue; + } + wms->registers[op_reg_index(name)] = (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemRegister, + .timestamp = reg.timestamp, + .data = { + .reg = { + .contents = (char **) reg.y_array, + .contents_size = (size_t) reg.y_size, + .type = (uint8_t) reg.y_type, + .width = (size_t) (reg.y_type == MBLOCK ? reg.y_width : 0), + .additional_data = reg.additional_data, + .name = name, + } + } } + }; + } while(reg_iter != NULL); + } + + // Initialize buffers + if (num_marked_files > 0) { + FOR_ALL_BUFFERS(buf) { + if (buf->b_ffname == NULL || SHADA_REMOVABLE(buf)) { + continue; } - shada_pack_entry(packer, *reg, max_kbyte); - if (did_convert) { - for (size_t i = 0; i < reg->data.reg.contents_size; i++) { - xfree(reg->data.reg.contents[i]); + const void *local_marks_iter = NULL; + const char *const fname = (const char *) buf->b_ffname; + khiter_t k; + int kh_ret; + k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret); + FileMarks *const filemarks = &kh_val(&wms->file_marks, k); + if (kh_ret > 0) { + kh_key(&wms->file_marks, k) = xstrdup(fname); + memset(filemarks, 0, sizeof(*filemarks)); + } + filemarks->is_local_entry = true; + do { + fmark_T fm; + char name; + local_marks_iter = mark_buffer_iter(local_marks_iter, buf, &name, &fm); + if (fm.mark.lnum == 0) { + break; + } + filemarks->marks[mark_local_index(name)] = (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemLocalMark, + .timestamp = fm.timestamp, + .data = { + .filemark = { + .mark = fm.mark, + .name = name, + .fname = (char *) fname, + .additional_data = fm.additional_data, + } + } + } + }; + if (fm.timestamp > filemarks->greatest_timestamp) { + filemarks->greatest_timestamp = fm.timestamp; + } + } while(local_marks_iter != NULL); + for (int i = 0; i < buf->b_changelistlen; i++) { + const fmark_T fm = buf->b_changelist[i]; + filemarks->changes[i] = (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemChange, + .timestamp = fm.timestamp, + .data = { + .filemark = { + .mark = fm.mark, + .fname = (char *) fname, + .additional_data = fm.additional_data, + } + } + } + }; + if (fm.timestamp > filemarks->greatest_timestamp) { + filemarks->greatest_timestamp = fm.timestamp; } - xfree(reg->data.reg.contents); } + filemarks->changes_size = (size_t) buf->b_changelistlen; } - xfree(registers); } - // 10. Variables - if (find_shada_parameter('!') != NULL) { - const void *var_iter = NULL; - const Timestamp cur_timestamp = os_time(); - do { - typval_T vartv; - const char *name; - var_iter = var_shada_iter(var_iter, &name, &vartv); - if (var_iter == NULL && vartv.v_type == VAR_UNKNOWN) { + if (sd_reader == NULL) { + goto shada_write_remaining; + } + + ShadaEntry entry; + while (shada_read_next_item(sd_reader, &entry, srni_flags, max_kbyte) + == NOTDONE) { +#define COMPARE_WITH_ENTRY(wms_entry_, entry) \ + do { \ + PossiblyFreedShadaEntry *const wms_entry = (wms_entry_); \ + if (wms_entry->data.type != kSDItemMissing) { \ + if (wms_entry->data.timestamp >= (entry).timestamp) { \ + shada_free_shada_entry(&(entry)); \ + break; \ + } \ + if (wms_entry->can_free_entry) { \ + shada_free_shada_entry(&wms_entry->data); \ + } \ + } \ + wms_entry->can_free_entry = true; \ + wms_entry->data = (entry); \ + } while (0) + switch (entry.type) { + case kSDItemMissing: { break; } - Object obj = vim_to_object(&vartv); - if (sd_writer->sd_conv.vc_type != CONV_NONE) { - convert_object(&sd_writer->sd_conv, &obj); + case kSDItemHeader: + case kSDItemBufferList: { + assert(false); } - shada_pack_entry(packer, (ShadaEntry) { - .type = kSDItemVariable, - .timestamp = cur_timestamp, - .data = { - .global_var = { - .name = (char *) name, - .value = obj, - .additional_elements = NULL, + case kSDItemUnknown: { + shada_pack_entry(packer, entry, 0); + shada_free_shada_entry(&entry); + break; + } + case kSDItemSearchPattern: { + COMPARE_WITH_ENTRY((entry.data.search_pattern.is_substitute_pattern + ? &wms->sub_search_pattern + : &wms->search_pattern), entry); + break; + } + case kSDItemSubString: { + COMPARE_WITH_ENTRY(&wms->replacement, entry); + break; + } + case kSDItemHistoryEntry: { + if (entry.data.history_item.histtype >= HIST_COUNT) { + shada_pack_entry(packer, entry, 0); + shada_free_shada_entry(&entry); + break; + } + hms_insert(&wms->hms[entry.data.history_item.histtype], entry, true, + true); + break; + } + case kSDItemRegister: { + const int idx = op_reg_index(entry.data.reg.name); + if (idx < 0) { + shada_pack_entry(packer, entry, 0); + shada_free_shada_entry(&entry); + break; + } + COMPARE_WITH_ENTRY(&wms->registers[idx], entry); + break; + } + case kSDItemVariable: { + if (!in_strset(&wms->dumped_variables, entry.data.global_var.name)) { + shada_pack_entry(packer, entry, 0); + } + shada_free_shada_entry(&entry); + break; + } + case kSDItemGlobalMark: { + const int idx = mark_global_index(entry.data.filemark.name); + if (idx < 0) { + shada_pack_entry(packer, entry, 0); + shada_free_shada_entry(&entry); + break; + } + COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry); + break; + } + case kSDItemChange: + case kSDItemLocalMark: { + if (shada_removable(entry.data.filemark.fname)) { + shada_free_shada_entry(&entry); + break; + } + const char *const fname = (const char *) entry.data.filemark.fname; + khiter_t k; + int kh_ret; + k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret); + FileMarks *const filemarks = &kh_val(&wms->file_marks, k); + if (kh_ret > 0) { + kh_key(&wms->file_marks, k) = xstrdup(fname); + memset(filemarks, 0, sizeof(*filemarks)); + } + if (entry.timestamp > filemarks->greatest_timestamp) { + filemarks->greatest_timestamp = entry.timestamp; + } + if (entry.type == kSDItemLocalMark) { + const int idx = mark_local_index(entry.data.filemark.name); + if (idx < 0) { + filemarks->additional_marks = xrealloc( + filemarks->additional_marks, + (++filemarks->additional_marks_size + * sizeof(filemarks->additional_marks[0]))); + filemarks->additional_marks[filemarks->additional_marks_size - 1] = + entry; + } else { + COMPARE_WITH_ENTRY(&filemarks->marks[idx], entry); + } + } else { + if (filemarks->is_local_entry) { + shada_free_shada_entry(&entry); + } else { + const int cl_len = (int) filemarks->changes_size; + int i; + for (i = cl_len; i > 0; i--) { + const ShadaEntry old_entry = filemarks->changes[i - 1].data; + if (old_entry.timestamp <= entry.timestamp) { + if (marks_equal(old_entry.data.filemark.mark, + entry.data.filemark.mark)) { + i = -1; + } + break; + } + } + if (i > 0) { + if (cl_len == JUMPLISTSIZE) { + if (filemarks->changes[0].can_free_entry) { + shada_free_shada_entry(&filemarks->changes[0].data); + } + memmove(&filemarks->changes[0], &filemarks->changes[1], + sizeof(filemarks->changes[0]) * (size_t) i); + } else if (i == 0) { + if (cl_len == JUMPLISTSIZE) { + i = -1; + } else { + memmove(&filemarks->changes[1], &filemarks->changes[0], + sizeof(filemarks->changes[0]) * (size_t) cl_len); + } + } + } + if (i != -1) { + filemarks->changes[i] = (PossiblyFreedShadaEntry) { + .can_free_entry = true, + .data = entry + }; + if (cl_len < JUMPLISTSIZE) { + filemarks->changes_size++; + } + } else { + shada_free_shada_entry(&entry); + } } } - }, max_kbyte); - api_free_object(obj); - clear_tv(&vartv); - } while (var_iter != NULL); + break; + } + case kSDItemJump: { + const int jl_len = (int) wms->jumps_size; + int i; + for (i = 0; i < jl_len; i++) { + const ShadaEntry old_entry = wms->jumps[i].data; + if (old_entry.timestamp >= entry.timestamp) { + if (marks_equal(old_entry.data.filemark.mark, + entry.data.filemark.mark) + && strcmp(old_entry.data.filemark.fname, + entry.data.filemark.fname) == 0) { + i = -1; + } + break; + } + } + if (i != -1) { + if (i < jl_len) { + if (jl_len == JUMPLISTSIZE) { + if (wms->jumps[0].can_free_entry) { + shada_free_shada_entry(&wms->jumps[0].data); + } + memmove(&wms->jumps[0], &wms->jumps[1], + sizeof(wms->jumps[0]) * (size_t) i); + } else { + memmove(&wms->jumps[i + 1], &wms->jumps[i], + sizeof(wms->jumps[0]) * (size_t) (jl_len - i)); + } + } else if (i == jl_len) { + if (jl_len == JUMPLISTSIZE) { + i = -1; + } else if (jl_len > 0) { + memmove(&wms->jumps[1], &wms->jumps[0], + sizeof(wms->jumps[0]) * (size_t) jl_len); + } + } + } + if (i != -1) { + wms->jumps[i] = (PossiblyFreedShadaEntry) { + .can_free_entry = true, + .data = entry, + }; + if (jl_len < JUMPLISTSIZE) { + wms->jumps_size++; + } + } else { + shada_free_shada_entry(&entry); + } + break; + } + } + } +#undef COMPARE_WITH_ENTRY + + // Write the rest +shada_write_remaining: +#define PACK_WMS_ARRAY(wms_array) \ + do { \ + for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \ + if (wms_array[i_].data.type != kSDItemMissing) { \ + shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_array[i_], \ + max_kbyte); \ + } \ + } \ + } while (0) + PACK_WMS_ARRAY(wms->global_marks); + PACK_WMS_ARRAY(wms->registers); + for (size_t i = 0; i < wms->jumps_size; i++) { + shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i], + max_kbyte); + } +#define PACK_WMS_ENTRY(wms_entry) \ + do { \ + if (wms_entry.data.type != kSDItemMissing) { \ + shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \ + max_kbyte); \ + } \ + } while (0) + PACK_WMS_ENTRY(wms->search_pattern); + PACK_WMS_ENTRY(wms->sub_search_pattern); + PACK_WMS_ENTRY(wms->replacement); +#undef PACK_WMS_ENTRY + + const size_t file_markss_size = kh_size(&wms->file_marks); + FileMarks **const all_file_markss = + xmalloc(file_markss_size * sizeof(*all_file_markss)); + FileMarks **cur_file_marks = all_file_markss; + for (khint_t i = kh_begin(&wms->file_marks); + i != kh_end(&wms->file_marks); + i++) { + if (kh_exist(&wms->file_marks, i)) { + *cur_file_marks++ = &kh_val(&wms->file_marks, i); + xfree((void *) kh_key(&wms->file_marks, i)); + } + } + qsort((void *) all_file_markss, file_markss_size, sizeof(*all_file_markss), + &compare_file_marks); + const size_t file_markss_to_dump = MIN(num_marked_files, file_markss_size); + 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++) { + shada_pack_encoded_entry(packer, &sd_writer->sd_conv, + all_file_markss[i]->changes[j], max_kbyte); + } + for (size_t j = 0; j < all_file_markss[i]->additional_marks_size; j++) { + shada_pack_entry(packer, all_file_markss[i]->additional_marks[j], 0); + shada_free_shada_entry(&all_file_markss[i]->additional_marks[j]); + } + xfree(all_file_markss[i]->additional_marks); + } + xfree(all_file_markss); +#undef PACK_WMS_ARRAY + + if (dump_history) { + for (size_t i = 0; i < HIST_COUNT; i++) { + if (dump_one_history[i]) { + hms_insert_whole_neovim_history(&wms->hms[i]); + HMS_ITER(&wms->hms[i], cur_entry) { + shada_pack_encoded_entry(packer, &sd_writer->sd_conv, + (PossiblyFreedShadaEntry) { + .data = cur_entry->data, + .can_free_entry = + cur_entry->can_free_entry, + }, max_kbyte); + } + hms_dealloc(&wms->hms[i]); + } + } } -#undef RUN_WITH_CONVERTED_STRING + kh_dealloc(file_marks, &wms->file_marks); kh_destroy(bufset, removable_bufs); msgpack_packer_free(packer); + kh_dealloc(strset, &wms->dumped_variables); + xfree(wms); } #undef PACK_STATIC_STR @@ -2162,7 +2675,7 @@ shada_write_file_nomerge: {} convert_setup(&sd_writer.sd_conv, p_enc, "utf-8"); - shada_write(&sd_writer, NULL); + shada_write(&sd_writer, (nomerge ? NULL : &sd_reader)); close_file((int)(intptr_t) sd_writer.cookie); @@ -2519,12 +3032,15 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, /// @param[out] entry Address where next entry contents will be saved. /// @param[in] flags Flags, determining whether and which items should be /// skipped (see SRNIFlags enum). +/// @param[in] max_kbyte If non-zero, skip reading entries which have length +/// greater then given. /// /// @return NOTDONE if entry was read correctly, FAIL if there were errors and /// OK at EOF. static int shada_read_next_item(ShaDaReadDef *const sd_reader, ShadaEntry *const entry, - const unsigned flags) + const unsigned flags, + const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { shada_read_next_item_start: @@ -2558,7 +3074,8 @@ shada_read_next_item_start: if ((type_u64 > SHADA_LAST_ENTRY ? !(flags & kSDReadUnknown) - : !((unsigned) (1 << type_u64) & flags))) { + : !((unsigned) (1 << type_u64) & flags)) + || (max_kbyte && length > max_kbyte * 1024)) { if (fread_len(sd_reader, NULL, length) != OK) { return FAIL; } @@ -3300,92 +3817,6 @@ shada_read_next_item_end: return NOTDONE; } -/// Return a list with all global marks set in current NeoVim instance -/// -/// List is not sorted. -/// -/// @warning Listed marks must be used before any buffer- or mark-editing -/// function is run. -/// -/// @param[in] removable_bufs Set of buffers on removable media. -/// -/// @return Array of ShadaEntry values, last one has type kSDItemMissing. -/// -/// @warning Resulting ShadaEntry values must not be freed. Returned -/// array must be freed with `xfree()`. -static ShadaEntry *list_global_marks( - const khash_t(bufset) *const removable_bufs) -{ - const void *iter = NULL; - const size_t nummarks = mark_global_amount(); - ShadaEntry *const ret = xmalloc(sizeof(ShadaEntry) * (nummarks + 1)); - ShadaEntry *cur = ret; - if (nummarks) { - do { - cur->type = kSDItemGlobalMark; - xfmark_T cur_fm; - iter = mark_global_iter(iter, &(cur->data.filemark.name), &cur_fm); - cur->data.filemark.mark = cur_fm.fmark.mark; - cur->data.filemark.additional_data = cur_fm.fmark.additional_data; - cur->timestamp = cur_fm.fmark.timestamp; - if (cur_fm.fmark.fnum == 0) { - if (cur_fm.fname == NULL) { - continue; - } - cur->data.filemark.fname = (char *) cur_fm.fname; - } else { - const buf_T *const buf = buflist_findnr(cur_fm.fmark.fnum); - if (buf == NULL || buf->b_ffname == NULL || SHADA_REMOVABLE(buf)) { - continue; - } else { - cur->data.filemark.fname = (char *) buf->b_ffname; - } - } - cur++; - } while(iter != NULL); - } - cur->type = kSDItemMissing; - return ret; -} - -/// Return a list with all buffer marks set in some buffer -/// -/// List is not sorted. -/// -/// @warning Listed marks must be used before any buffer- or mark-editing -/// function is run. -/// -/// @param[in] buf Buffer for which marks are listed. -/// -/// @return Array of ShadaEntry values, last one has type kSDItemMissing. -/// -/// @warning Resulting ShadaEntry values must not be freed. Returned -/// array must be freed with `xfree()`. -static ShadaEntry *list_buffer_marks(const buf_T *const buf) -{ - const char *const fname = (char *) buf->b_ffname; - const void *iter = NULL; - const size_t nummarks = mark_buffer_amount(buf); - ShadaEntry *const ret = xmalloc(sizeof(ShadaEntry) * (nummarks + 1)); - ShadaEntry *cur = ret; - if (nummarks) { - do { - cur->type = kSDItemLocalMark; - fmark_T cur_fm; - iter = mark_buffer_iter(iter, buf, &(cur->data.filemark.name), &cur_fm); - cur->data.filemark.mark = cur_fm.mark; - cur->data.filemark.fname = (char *) fname; - cur->data.filemark.additional_data = cur_fm.additional_data; - cur->timestamp = cur_fm.timestamp; - if (cur->data.filemark.mark.lnum != 0) { - cur++; - } - } while(iter != NULL); - } - cur->type = kSDItemMissing; - return ret; -} - /// Check whether "name" is on removable media (according to 'shada') /// /// @param[in] name Checked name. @@ -3414,52 +3845,6 @@ bool shada_removable(const char *name) return retval; } -/// Return a list with all registers and their contents -/// -/// List is not sorted. -/// -/// @warning Listed registers must be used before any register-editing function -/// is run. -/// -/// @param[in] max_num_lines Maximum number of lines in the register. If it is -/// zero then all registers are listed. -/// -/// @return Array of ShadaEntry values, last one has type kSDItemMissing. -/// -/// @warning Resulting ShadaEntry values must not be freed. Returned -/// array must be freed with `xfree()`. -static ShadaEntry *list_registers(const size_t max_num_lines) -{ - const void *iter = NULL; - const size_t numregs = op_register_amount(); - ShadaEntry *const ret = xmalloc(sizeof(ShadaEntry) * (numregs + 1)); - ShadaEntry *cur = ret; - if (numregs) { - do { - cur->type = kSDItemRegister; - yankreg_T cur_reg; - iter = op_register_iter(iter, &(cur->data.reg.name), &cur_reg); - if (max_num_lines && (size_t) cur_reg.y_size > max_num_lines) { - continue; - } - cur->data.reg.contents = (char **) cur_reg.y_array; - cur->data.reg.type = (uint8_t) cur_reg.y_type; - cur->data.reg.contents_size = (size_t) cur_reg.y_size; - if (cur_reg.y_type == MBLOCK) { - cur->data.reg.width = (size_t) cur_reg.y_width; - } else { - cur->data.reg.width = 0; - } - cur->data.reg.additional_data = cur_reg.additional_data; - cur->timestamp = cur_reg.timestamp; - cur++; - } while(iter != NULL); - } - cur->type = kSDItemMissing; - return ret; -} - - #if 0 static int viminfo_errcnt; |