// This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com // Tree data structure for storing marks at (row, col) positions and updating // them to arbitrary text changes. Derivative work of kbtree in klib, whose // copyright notice is reproduced below. Also inspired by the design of the // marker tree data structure of the Atom editor, regarding efficient updates // to text changes. // // Marks are inserted using marktree_put. Text changes are processed using // marktree_splice. All read and delete operations use the iterator. // use marktree_itr_get to put an iterator at a given position or // marktree_lookup to lookup a mark by its id (iterator optional in this case). // Use marktree_itr_current and marktree_itr_next/prev to read marks in a loop. // marktree_del_itr deletes the current mark of the iterator and implicitly // moves the iterator to the next mark. // // Work is ongoing to fully support ranges (mark pairs). // Copyright notice for kbtree (included in heavily modified form): // // Copyright 1997-1999, 2001, John-Mark Gurney. // 2008-2009, Attractive Chaos // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. // // Changes done by by the neovim project follow the Apache v2 license available // at the repo root. #include #include "nvim/garray.h" #include "nvim/lib/kvec.h" #include "nvim/marktree.h" #define T MT_BRANCH_FACTOR #define ILEN (sizeof(mtnode_t) + (2 * T) * sizeof(void *)) #define ID_INCR (((uint64_t)1) << 2) #define rawkey(itr) ((itr)->node->key[(itr)->i]) static bool pos_leq(mtpos_t a, mtpos_t b) { return a.row < b.row || (a.row == b.row && a.col <= b.col); } static void relative(mtpos_t base, mtpos_t *val) { assert(pos_leq(base, *val)); if (val->row == base.row) { val->row = 0; val->col -= base.col; } else { val->row -= base.row; } } static void unrelative(mtpos_t base, mtpos_t *val) { if (val->row == 0) { val->row = base.row; val->col += base.col; } else { val->row += base.row; } } static void compose(mtpos_t *base, mtpos_t val) { if (val.row == 0) { base->col += val.col; } else { base->row += val.row; base->col = val.col; } } #ifdef INCLUDE_GENERATED_DECLARATIONS # include "marktree.c.generated.h" #endif #define mt_generic_cmp(a, b) (((b) < (a)) - ((a) < (b))) static int key_cmp(mtkey_t a, mtkey_t b) { int cmp = mt_generic_cmp(a.pos.row, b.pos.row); if (cmp != 0) { return cmp; } cmp = mt_generic_cmp(a.pos.col, b.pos.col); if (cmp != 0) { return cmp; } // NB: keeping the events at the same pos sorted by id is actually not // necessary only make sure that START is before END etc. return mt_generic_cmp(a.flags, b.flags); } static inline int marktree_getp_aux(const mtnode_t *x, mtkey_t k, int *r) { int tr, *rr, begin = 0, end = x->n; if (x->n == 0) { return -1; } rr = r? r : &tr; while (begin < end) { int mid = (begin + end) >> 1; if (key_cmp(x->key[mid], k) < 0) { begin = mid + 1; } else { end = mid; } } if (begin == x->n) { *rr = 1; return x->n - 1; } if ((*rr = key_cmp(k, x->key[begin])) < 0) { begin--; } return begin; } static inline void refkey(MarkTree *b, mtnode_t *x, int i) { pmap_put(uint64_t)(b->id2node, mt_lookup_key(x->key[i]), x); } // put functions // x must be an internal node, which is not full // x->ptr[i] should be a full node, i e x->ptr[i]->n == 2*T-1 static inline void split_node(MarkTree *b, mtnode_t *x, const int i) { mtnode_t *y = x->ptr[i]; mtnode_t *z; z = (mtnode_t *)xcalloc(1, y->level ? ILEN : sizeof(mtnode_t)); b->n_nodes++; z->level = y->level; z->n = T - 1; memcpy(z->key, &y->key[T], sizeof(mtkey_t) * (T - 1)); for (int j = 0; j < T - 1; j++) { refkey(b, z, j); } if (y->level) { memcpy(z->ptr, &y->ptr[T], sizeof(mtnode_t *) * T); for (int j = 0; j < T; j++) { z->ptr[j]->parent = z; } } y->n = T - 1; memmove(&x->ptr[i + 2], &x->ptr[i + 1], sizeof(mtnode_t *) * (size_t)(x->n - i)); x->ptr[i + 1] = z; z->parent = x; // == y->parent memmove(&x->key[i + 1], &x->key[i], sizeof(mtkey_t) * (size_t)(x->n - i)); // move key to internal layer: x->key[i] = y->key[T - 1]; refkey(b, x, i); x->n++; for (int j = 0; j < T - 1; j++) { relative(x->key[i].pos, &z->key[j].pos); } if (i > 0) { unrelative(x->key[i - 1].pos, &x->key[i].pos); } } // x must not be a full node (even if there might be internal space) static inline void marktree_putp_aux(MarkTree *b, mtnode_t *x, mtkey_t k) { int i; if (x->level == 0) { i = marktree_getp_aux(x, k, 0); if (i != x->n - 1) { memmove(&x->key[i + 2], &x->key[i + 1], (size_t)(x->n - i - 1) * sizeof(mtkey_t)); } x->key[i + 1] = k; refkey(b, x, i + 1); x->n++; } else { i = marktree_getp_aux(x, k, 0) + 1; if (x->ptr[i]->n == 2 * T - 1) { split_node(b, x, i); if (key_cmp(k, x->key[i]) > 0) { i++; } } if (i > 0) { relative(x->key[i - 1].pos, &k.pos); } marktree_putp_aux(b, x->ptr[i], k); } } void marktree_put(MarkTree *b, mtkey_t key, int end_row, int end_col, bool end_right) { assert(!(key.flags & ~MT_FLAG_EXTERNAL_MASK)); if (end_row >= 0) { key.flags |= MT_FLAG_PAIRED; } marktree_put_key(b, key); if (end_row >= 0) { mtkey_t end_key = key; end_key.flags = (uint16_t)((uint16_t)(key.flags & ~MT_FLAG_RIGHT_GRAVITY) |(uint16_t)MT_FLAG_END |(uint16_t)(end_right ? MT_FLAG_RIGHT_GRAVITY : 0)); end_key.pos = (mtpos_t){ end_row, end_col }; marktree_put_key(b, end_key); } } void marktree_put_key(MarkTree *b, mtkey_t k) { k.flags |= MT_FLAG_REAL; // let's be real. if (!b->root) { b->root = (mtnode_t *)xcalloc(1, ILEN); b->n_nodes++; } mtnode_t *r, *s; b->n_keys++; r = b->root; if (r->n == 2 * T - 1) { b->n_nodes++; s = (mtnode_t *)xcalloc(1, ILEN); b->root = s; s->level = r->level + 1; s->n = 0; s->ptr[0] = r; r->parent = s; split_node(b, s, 0); r = s; } marktree_putp_aux(b, r, k); } /// INITIATING DELETION PROTOCOL: /// /// 1. Construct a valid iterator to the node to delete (argument) /// 2. If an "internal" key. Iterate one step to the left or right, /// which gives an internal key "auxiliary key". /// 3. Now delete this internal key (intended or auxiliary). /// The leaf node X might become undersized. /// 4. If step two was done: now replace the key that _should_ be /// deleted with the auxiliary key. Adjust relative /// 5. Now "repair" the tree as needed. We always start at a leaf node X. /// - if the node is big enough, terminate /// - if we can steal from the left, steal /// - if we can steal from the right, steal /// - otherwise merge this node with a neighbour. This might make our /// parent undersized. So repeat 5 for the parent. /// 6. If 4 went all the way to the root node. The root node /// might have ended up with size 0. Delete it then. /// /// NB: ideally keeps the iterator valid. Like point to the key after this /// if present. /// /// @param rev should be true if we plan to iterate _backwards_ and delete /// stuff before this key. Most of the time this is false (the /// recommended strategy is to always iterate forward) void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) { int adjustment = 0; mtnode_t *cur = itr->node; int curi = itr->i; uint64_t id = mt_lookup_key(cur->key[curi]); // fprintf(stderr, "\nDELET %lu\n", id); if (itr->node->level) { if (rev) { abort(); } else { // fprintf(stderr, "INTERNAL %d\n", cur->level); // steal previous node marktree_itr_prev(b, itr); adjustment = -1; } } // 3. mtnode_t *x = itr->node; assert(x->level == 0); mtkey_t intkey = x->key[itr->i]; if (x->n > itr->i + 1) { memmove(&x->key[itr->i], &x->key[itr->i + 1], sizeof(mtkey_t) * (size_t)(x->n - itr->i - 1)); } x->n--; // 4. // if (adjustment == 1) { // abort(); // } if (adjustment == -1) { int ilvl = itr->lvl - 1; const mtnode_t *lnode = x; do { const mtnode_t *const p = lnode->parent; if (ilvl < 0) { abort(); } const int i = itr->s[ilvl].i; assert(p->ptr[i] == lnode); if (i > 0) { unrelative(p->key[i - 1].pos, &intkey.pos); } lnode = p; ilvl--; } while (lnode != cur); mtkey_t deleted = cur->key[curi]; cur->key[curi] = intkey; refkey(b, cur, curi); relative(intkey.pos, &deleted.pos); mtnode_t *y = cur->ptr[curi + 1]; if (deleted.pos.row || deleted.pos.col) { while (y) { for (int k = 0; k < y->n; k++) { unrelative(deleted.pos, &y->key[k].pos); } y = y->level ? y->ptr[0] : NULL; } } itr->i--; } b->n_keys--; pmap_del(uint64_t)(b->id2node, id); // 5. bool itr_dirty = false; int rlvl = itr->lvl - 1; int *lasti = &itr->i; while (x != b->root) { assert(rlvl >= 0); mtnode_t *p = x->parent; if (x->n >= T - 1) { // we are done, if this node is fine the rest of the tree will be break; } int pi = itr->s[rlvl].i; assert(p->ptr[pi] == x); if (pi > 0 && p->ptr[pi - 1]->n > T - 1) { *lasti += 1; itr_dirty = true; // steal one key from the left neighbour pivot_right(b, p, pi - 1); break; } else if (pi < p->n && p->ptr[pi + 1]->n > T - 1) { // steal one key from right neighbour pivot_left(b, p, pi); break; } else if (pi > 0) { // fprintf(stderr, "LEFT "); assert(p->ptr[pi - 1]->n == T - 1); // merge with left neighbour *lasti += T; x = merge_node(b, p, pi - 1); if (lasti == &itr->i) { // TRICKY: we merged the node the iterator was on itr->node = x; } itr->s[rlvl].i--; itr_dirty = true; } else { // fprintf(stderr, "RIGHT "); assert(pi < p->n && p->ptr[pi + 1]->n == T - 1); merge_node(b, p, pi); // no iter adjustment needed } lasti = &itr->s[rlvl].i; rlvl--; x = p; } // 6. if (b->root->n == 0) { if (itr->lvl > 0) { memmove(itr->s, itr->s + 1, (size_t)(itr->lvl - 1) * sizeof(*itr->s)); itr->lvl--; } if (b->root->level) { mtnode_t *oldroot = b->root; b->root = b->root->ptr[0]; b->root->parent = NULL; xfree(oldroot); } else { // no items, nothing for iterator to point to // not strictly needed, should handle delete right-most mark anyway itr->node = NULL; } } if (itr->node && itr_dirty) { marktree_itr_fix_pos(b, itr); } // BONUS STEP: fix the iterator, so that it points to the key afterwards // TODO(bfredl): with "rev" should point before // if (adjustment == 1) { // abort(); // } if (adjustment == -1) { // tricky: we stand at the deleted space in the previous leaf node. // But the inner key is now the previous key we stole, so we need // to skip that one as well. marktree_itr_next(b, itr); marktree_itr_next(b, itr); } else { if (itr->node && itr->i >= itr->node->n) { // we deleted the last key of a leaf node // go to the inner key after that. assert(itr->node->level == 0); marktree_itr_next(b, itr); } } } static mtnode_t *merge_node(MarkTree *b, mtnode_t *p, int i) { mtnode_t *x = p->ptr[i], *y = p->ptr[i + 1]; x->key[x->n] = p->key[i]; refkey(b, x, x->n); if (i > 0) { relative(p->key[i - 1].pos, &x->key[x->n].pos); } memmove(&x->key[x->n + 1], y->key, (size_t)y->n * sizeof(mtkey_t)); for (int k = 0; k < y->n; k++) { refkey(b, x, x->n + 1 + k); unrelative(x->key[x->n].pos, &x->key[x->n + 1 + k].pos); } if (x->level) { memmove(&x->ptr[x->n + 1], y->ptr, ((size_t)y->n + 1) * sizeof(mtnode_t *)); for (int k = 0; k < y->n + 1; k++) { x->ptr[x->n + k + 1]->parent = x; } } x->n += y->n + 1; memmove(&p->key[i], &p->key[i + 1], (size_t)(p->n - i - 1) * sizeof(mtkey_t)); memmove(&p->ptr[i + 1], &p->ptr[i + 2], (size_t)(p->n - i - 1) * sizeof(mtkey_t *)); p->n--; xfree(y); b->n_nodes--; return x; } // TODO(bfredl): as a potential "micro" optimization, pivoting should balance // the two nodes instead of stealing just one key static void pivot_right(MarkTree *b, mtnode_t *p, int i) { mtnode_t *x = p->ptr[i], *y = p->ptr[i + 1]; memmove(&y->key[1], y->key, (size_t)y->n * sizeof(mtkey_t)); if (y->level) { memmove(&y->ptr[1], y->ptr, ((size_t)y->n + 1) * sizeof(mtnode_t *)); } y->key[0] = p->key[i]; refkey(b, y, 0); p->key[i] = x->key[x->n - 1]; refkey(b, p, i); if (x->level) { y->ptr[0] = x->ptr[x->n]; y->ptr[0]->parent = y; } x->n--; y->n++; if (i > 0) { unrelative(p->key[i - 1].pos, &p->key[i].pos); } relative(p->key[i].pos, &y->key[0].pos); for (int k = 1; k < y->n; k++) { unrelative(y->key[0].pos, &y->key[k].pos); } } static void pivot_left(MarkTree *b, mtnode_t *p, int i) { mtnode_t *x = p->ptr[i], *y = p->ptr[i + 1]; // reverse from how we "always" do it. but pivot_left // is just the inverse of pivot_right, so reverse it literally. for (int k = 1; k < y->n; k++) { relative(y->key[0].pos, &y->key[k].pos); } unrelative(p->key[i].pos, &y->key[0].pos); if (i > 0) { relative(p->key[i - 1].pos, &p->key[i].pos); } x->key[x->n] = p->key[i]; refkey(b, x, x->n); p->key[i] = y->key[0]; refkey(b, p, i); if (x->level) { x->ptr[x->n + 1] = y->ptr[0]; x->ptr[x->n + 1]->parent = x; } memmove(y->key, &y->key[1], (size_t)(y->n - 1) * sizeof(mtkey_t)); if (y->level) { memmove(y->ptr, &y->ptr[1], (size_t)y->n * sizeof(mtnode_t *)); } x->n++; y->n--; } /// frees all mem, resets tree to valid empty state void marktree_clear(MarkTree *b) { if (b->root) { marktree_free_node(b->root); b->root = NULL; } if (b->id2node->table.keys) { pmap_destroy(uint64_t)(b->id2node); pmap_init(uint64_t, b->id2node); } b->n_keys = 0; b->n_nodes = 0; } void marktree_free_node(mtnode_t *x) { if (x->level) { for (int i = 0; i < x->n + 1; i++) { marktree_free_node(x->ptr[i]); } } xfree(x); } /// NB: caller must check not pair! void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, mtkey_t key) { // TODO(bfredl): clean up this mess and re-instantiate &= and |= forms // once we upgrade to a non-broken version of gcc in functionaltest-lua CI rawkey(itr).flags = (uint16_t)(rawkey(itr).flags & (uint16_t) ~MT_FLAG_DECOR_MASK); rawkey(itr).flags = (uint16_t)(rawkey(itr).flags | (uint16_t)(decor_level << MT_FLAG_DECOR_OFFSET) | (uint16_t)(key.flags & MT_FLAG_DECOR_MASK)); rawkey(itr).decor_full = key.decor_full; rawkey(itr).hl_id = key.hl_id; rawkey(itr).priority = key.priority; } void marktree_move(MarkTree *b, MarkTreeIter *itr, int row, int col) { mtkey_t key = rawkey(itr); // TODO(bfredl): optimize when moving a mark within a leaf without moving it // across neighbours! marktree_del_itr(b, itr, false); key.pos = (mtpos_t){ row, col }; marktree_put_key(b, key); itr->node = NULL; // itr might become invalid by put } // itr functions // TODO(bfredl): static inline? bool marktree_itr_get(MarkTree *b, int32_t row, int col, MarkTreeIter *itr) { return marktree_itr_get_ext(b, (mtpos_t){ row, col }, itr, false, false, NULL); } bool marktree_itr_get_ext(MarkTree *b, mtpos_t p, MarkTreeIter *itr, bool last, bool gravity, mtpos_t *oldbase) { if (b->n_keys == 0) { itr->node = NULL; return false; } mtkey_t k = { .pos = p, .flags = gravity ? MT_FLAG_RIGHT_GRAVITY : 0 }; if (last && !gravity) { k.flags = MT_FLAG_LAST; } itr->pos = (mtpos_t){ 0, 0 }; itr->node = b->root; itr->lvl = 0; if (oldbase) { oldbase[itr->lvl] = itr->pos; } while (true) { itr->i = marktree_getp_aux(itr->node, k, 0) + 1; if (itr->node->level == 0) { break; } itr->s[itr->lvl].i = itr->i; itr->s[itr->lvl].oldcol = itr->pos.col; if (itr->i > 0) { compose(&itr->pos, itr->node->key[itr->i - 1].pos); relative(itr->node->key[itr->i - 1].pos, &k.pos); } itr->node = itr->node->ptr[itr->i]; itr->lvl++; if (oldbase) { oldbase[itr->lvl] = itr->pos; } } if (last) { return marktree_itr_prev(b, itr); } else if (itr->i >= itr->node->n) { return marktree_itr_next(b, itr); } return true; } bool marktree_itr_first(MarkTree *b, MarkTreeIter *itr) { itr->node = b->root; if (b->n_keys == 0) { return false; } itr->i = 0; itr->lvl = 0; itr->pos = (mtpos_t){ 0, 0 }; while (itr->node->level > 0) { itr->s[itr->lvl].i = 0; itr->s[itr->lvl].oldcol = 0; itr->lvl++; itr->node = itr->node->ptr[0]; } return true; } // gives the first key that is greater or equal to p int marktree_itr_last(MarkTree *b, MarkTreeIter *itr) { if (b->n_keys == 0) { itr->node = NULL; return false; } itr->pos = (mtpos_t){ 0, 0 }; itr->node = b->root; itr->lvl = 0; while (true) { itr->i = itr->node->n; if (itr->node->level == 0) { break; } itr->s[itr->lvl].i = itr->i; itr->s[itr->lvl].oldcol = itr->pos.col; assert(itr->i > 0); compose(&itr->pos, itr->node->key[itr->i - 1].pos); itr->node = itr->node->ptr[itr->i]; itr->lvl++; } itr->i--; return true; } // TODO(bfredl): static inline bool marktree_itr_next(MarkTree *b, MarkTreeIter *itr) { return marktree_itr_next_skip(b, itr, false, NULL); } static bool marktree_itr_next_skip(MarkTree *b, MarkTreeIter *itr, bool skip, mtpos_t oldbase[]) { if (!itr->node) { return false; } itr->i++; if (itr->node->level == 0 || skip) { if (itr->i < itr->node->n) { // TODO(bfredl): this is the common case, // and could be handled by inline wrapper return true; } // we ran out of non-internal keys. Go up until we find an internal key while (itr->i >= itr->node->n) { itr->node = itr->node->parent; if (itr->node == NULL) { return false; } itr->lvl--; itr->i = itr->s[itr->lvl].i; if (itr->i > 0) { itr->pos.row -= itr->node->key[itr->i - 1].pos.row; itr->pos.col = itr->s[itr->lvl].oldcol; } } } else { // we stood at an "internal" key. Go down to the first non-internal // key after it. while (itr->node->level > 0) { // internal key, there is always a child after if (itr->i > 0) { itr->s[itr->lvl].oldcol = itr->pos.col; compose(&itr->pos, itr->node->key[itr->i - 1].pos); } if (oldbase && itr->i == 0) { oldbase[itr->lvl + 1] = oldbase[itr->lvl]; } itr->s[itr->lvl].i = itr->i; assert(itr->node->ptr[itr->i]->parent == itr->node); itr->node = itr->node->ptr[itr->i]; itr->i = 0; itr->lvl++; } } return true; } bool marktree_itr_prev(MarkTree *b, MarkTreeIter *itr) { if (!itr->node) { return false; } if (itr->node->level == 0) { itr->i--; if (itr->i >= 0) { // TODO(bfredl): this is the common case, // and could be handled by inline wrapper return true; } // we ran out of non-internal keys. Go up until we find a non-internal key while (itr->i < 0) { itr->node = itr->node->parent; if (itr->node == NULL) { return false; } itr->lvl--; itr->i = itr->s[itr->lvl].i - 1; if (itr->i >= 0) { itr->pos.row -= itr->node->key[itr->i].pos.row; itr->pos.col = itr->s[itr->lvl].oldcol; } } } else { // we stood at an "internal" key. Go down to the last non-internal // key before it. while (itr->node->level > 0) { // internal key, there is always a child before if (itr->i > 0) { itr->s[itr->lvl].oldcol = itr->pos.col; compose(&itr->pos, itr->node->key[itr->i - 1].pos); } itr->s[itr->lvl].i = itr->i; assert(itr->node->ptr[itr->i]->parent == itr->node); itr->node = itr->node->ptr[itr->i]; itr->i = itr->node->n; itr->lvl++; } itr->i--; } return true; } void marktree_itr_rewind(MarkTree *b, MarkTreeIter *itr) { if (!itr->node) { return; } if (itr->node->level) { marktree_itr_prev(b, itr); } itr->i = 0; } bool marktree_itr_node_done(MarkTreeIter *itr) { return !itr->node || itr->i == itr->node->n - 1; } mtpos_t marktree_itr_pos(MarkTreeIter *itr) { mtpos_t pos = rawkey(itr).pos; unrelative(itr->pos, &pos); return pos; } mtkey_t marktree_itr_current(MarkTreeIter *itr) { if (itr->node) { mtkey_t key = rawkey(itr); key.pos = marktree_itr_pos(itr); return key; } return MT_INVALID_KEY; } static bool itr_eq(MarkTreeIter *itr1, MarkTreeIter *itr2) { return (&rawkey(itr1) == &rawkey(itr2)); } static void itr_swap(MarkTreeIter *itr1, MarkTreeIter *itr2) { mtkey_t key1 = rawkey(itr1); mtkey_t key2 = rawkey(itr2); rawkey(itr1) = key2; rawkey(itr1).pos = key1.pos; rawkey(itr2) = key1; rawkey(itr2).pos = key2.pos; } bool marktree_splice(MarkTree *b, int32_t start_line, int start_col, int old_extent_line, int old_extent_col, int new_extent_line, int new_extent_col) { mtpos_t start = { start_line, start_col }; mtpos_t old_extent = { old_extent_line, old_extent_col }; mtpos_t new_extent = { new_extent_line, new_extent_col }; bool may_delete = (old_extent.row != 0 || old_extent.col != 0); bool same_line = old_extent.row == 0 && new_extent.row == 0; unrelative(start, &old_extent); unrelative(start, &new_extent); MarkTreeIter itr[1] = { 0 }; MarkTreeIter enditr[1] = { 0 }; mtpos_t oldbase[MT_MAX_DEPTH] = { 0 }; marktree_itr_get_ext(b, start, itr, false, true, oldbase); if (!itr->node) { // den e FÄRDIG return false; } mtpos_t delta = { new_extent.row - old_extent.row, new_extent.col - old_extent.col }; if (may_delete) { mtpos_t ipos = marktree_itr_pos(itr); if (!pos_leq(old_extent, ipos) || (old_extent.row == ipos.row && old_extent.col == ipos.col && !mt_right(rawkey(itr)))) { marktree_itr_get_ext(b, old_extent, enditr, true, true, NULL); assert(enditr->node); // "assert" (itr <= enditr) } else { may_delete = false; } } bool past_right = false; bool moved = false; // Follow the general strategy of messing things up and fix them later // "oldbase" carries the information needed to calculate old position of // children. if (may_delete) { while (itr->node && !past_right) { mtpos_t loc_start = start; mtpos_t loc_old = old_extent; relative(itr->pos, &loc_start); relative(oldbase[itr->lvl], &loc_old); continue_same_node: // NB: strictly should be less than the right gravity of loc_old, but // the iter comparison below will already break on that. if (!pos_leq(rawkey(itr).pos, loc_old)) { break; } if (mt_right(rawkey(itr))) { while (!itr_eq(itr, enditr) && mt_right(rawkey(enditr))) { marktree_itr_prev(b, enditr); } if (!mt_right(rawkey(enditr))) { itr_swap(itr, enditr); refkey(b, itr->node, itr->i); refkey(b, enditr->node, enditr->i); } else { past_right = true; // NOLINT (void)past_right; break; } } if (itr_eq(itr, enditr)) { // actually, will be past_right after this key past_right = true; } moved = true; if (itr->node->level) { oldbase[itr->lvl + 1] = rawkey(itr).pos; unrelative(oldbase[itr->lvl], &oldbase[itr->lvl + 1]); rawkey(itr).pos = loc_start; marktree_itr_next_skip(b, itr, false, oldbase); } else { rawkey(itr).pos = loc_start; if (itr->i < itr->node->n - 1) { itr->i++; if (!past_right) { goto continue_same_node; } } else { marktree_itr_next(b, itr); } } } while (itr->node) { mtpos_t loc_new = new_extent; relative(itr->pos, &loc_new); mtpos_t limit = old_extent; relative(oldbase[itr->lvl], &limit); past_continue_same_node: if (pos_leq(limit, rawkey(itr).pos)) { break; } mtpos_t oldpos = rawkey(itr).pos; rawkey(itr).pos = loc_new; moved = true; if (itr->node->level) { oldbase[itr->lvl + 1] = oldpos; unrelative(oldbase[itr->lvl], &oldbase[itr->lvl + 1]); marktree_itr_next_skip(b, itr, false, oldbase); } else { if (itr->i < itr->node->n - 1) { itr->i++; goto past_continue_same_node; } else { marktree_itr_next(b, itr); } } } } while (itr->node) { unrelative(oldbase[itr->lvl], &rawkey(itr).pos); int realrow = rawkey(itr).pos.row; assert(realrow >= old_extent.row); bool done = false; if (realrow == old_extent.row) { if (delta.col) { rawkey(itr).pos.col += delta.col; moved = true; } } else { if (same_line) { // optimization: column only adjustment can skip remaining rows done = true; } } if (delta.row) { rawkey(itr).pos.row += delta.row; moved = true; } relative(itr->pos, &rawkey(itr).pos); if (done) { break; } marktree_itr_next_skip(b, itr, true, NULL); } return moved; } void marktree_move_region(MarkTree *b, int start_row, colnr_T start_col, int extent_row, colnr_T extent_col, int new_row, colnr_T new_col) { mtpos_t start = { start_row, start_col }, size = { extent_row, extent_col }; mtpos_t end = size; unrelative(start, &end); MarkTreeIter itr[1] = { 0 }; marktree_itr_get_ext(b, start, itr, false, true, NULL); kvec_t(mtkey_t) saved = KV_INITIAL_VALUE; while (itr->node) { mtkey_t k = marktree_itr_current(itr); if (!pos_leq(k.pos, end) || (k.pos.row == end.row && k.pos.col == end.col && mt_right(k))) { break; } relative(start, &k.pos); kv_push(saved, k); marktree_del_itr(b, itr, false); } marktree_splice(b, start.row, start.col, size.row, size.col, 0, 0); mtpos_t new = { new_row, new_col }; marktree_splice(b, new.row, new.col, 0, 0, size.row, size.col); for (size_t i = 0; i < kv_size(saved); i++) { mtkey_t item = kv_A(saved, i); unrelative(new, &item.pos); marktree_put_key(b, item); } kv_destroy(saved); } /// @param itr OPTIONAL. set itr to pos. mtkey_t marktree_lookup_ns(MarkTree *b, uint32_t ns, uint32_t id, bool end, MarkTreeIter *itr) { return marktree_lookup(b, mt_lookup_id(ns, id, end), itr); } /// @param itr OPTIONAL. set itr to pos. mtkey_t marktree_lookup(MarkTree *b, uint64_t id, MarkTreeIter *itr) { mtnode_t *n = pmap_get(uint64_t)(b->id2node, id); if (n == NULL) { if (itr) { itr->node = NULL; } return MT_INVALID_KEY; } int i = 0; for (i = 0; i < n->n; i++) { if (mt_lookup_key(n->key[i]) == id) { goto found; } } abort(); found: {} mtkey_t key = n->key[i]; if (itr) { itr->i = i; itr->node = n; itr->lvl = b->root->level - n->level; } while (n->parent != NULL) { mtnode_t *p = n->parent; for (i = 0; i < p->n + 1; i++) { if (p->ptr[i] == n) { goto found_node; } } abort(); found_node: if (itr) { itr->s[b->root->level - p->level].i = i; } if (i > 0) { unrelative(p->key[i - 1].pos, &key.pos); } n = p; } if (itr) { marktree_itr_fix_pos(b, itr); } return key; } mtpos_t marktree_get_altpos(MarkTree *b, mtkey_t mark, MarkTreeIter *itr) { return marktree_get_alt(b, mark, itr).pos; } mtkey_t marktree_get_alt(MarkTree *b, mtkey_t mark, MarkTreeIter *itr) { mtkey_t end = MT_INVALID_KEY; if (mt_paired(mark)) { end = marktree_lookup_ns(b, mark.ns, mark.id, !mt_end(mark), itr); } return end; } static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr) { itr->pos = (mtpos_t){ 0, 0 }; mtnode_t *x = b->root; for (int lvl = 0; lvl < itr->lvl; lvl++) { itr->s[lvl].oldcol = itr->pos.col; int i = itr->s[lvl].i; if (i > 0) { compose(&itr->pos, x->key[i - 1].pos); } assert(x->level); x = x->ptr[i]; } assert(x == itr->node); } // for unit test void marktree_put_test(MarkTree *b, uint32_t id, int row, int col, bool right_gravity) { mtkey_t key = { { row, col }, UINT32_MAX, id, 0, mt_flags(right_gravity, 0), 0, NULL }; marktree_put(b, key, -1, -1, false); } // for unit test bool mt_right_test(mtkey_t key) { return mt_right(key); } void marktree_check(MarkTree *b) { #ifndef NDEBUG if (b->root == NULL) { assert(b->n_keys == 0); assert(b->n_nodes == 0); assert(b->id2node == NULL || map_size(b->id2node) == 0); return; } mtpos_t dummy; bool last_right = false; size_t nkeys = check_node(b, b->root, &dummy, &last_right); assert(b->n_keys == nkeys); assert(b->n_keys == map_size(b->id2node)); #else // Do nothing, as assertions are required (void)b; #endif } #ifndef NDEBUG static size_t check_node(MarkTree *b, mtnode_t *x, mtpos_t *last, bool *last_right) { assert(x->n <= 2 * T - 1); // TODO(bfredl): too strict if checking "in repair" post-delete tree. assert(x->n >= (x != b->root ? T - 1 : 0)); size_t n_keys = (size_t)x->n; for (int i = 0; i < x->n; i++) { if (x->level) { n_keys += check_node(b, x->ptr[i], last, last_right); } else { *last = (mtpos_t) { 0, 0 }; } if (i > 0) { unrelative(x->key[i - 1].pos, last); } assert(pos_leq(*last, x->key[i].pos)); if (last->row == x->key[i].pos.row && last->col == x->key[i].pos.col) { assert(!*last_right || mt_right(x->key[i])); } *last_right = mt_right(x->key[i]); assert(x->key[i].pos.col >= 0); assert(pmap_get(uint64_t)(b->id2node, mt_lookup_key(x->key[i])) == x); } if (x->level) { n_keys += check_node(b, x->ptr[x->n], last, last_right); unrelative(x->key[x->n - 1].pos, last); for (int i = 0; i < x->n + 1; i++) { assert(x->ptr[i]->parent == x); assert(x->ptr[i]->level == x->level - 1); // PARANOIA: check no double node ref for (int j = 0; j < i; j++) { assert(x->ptr[i] != x->ptr[j]); } } } else { *last = x->key[x->n - 1].pos; } return n_keys; } #endif char *mt_inspect_rec(MarkTree *b) { garray_T ga; ga_init(&ga, (int)sizeof(char), 80); mtpos_t p = { 0, 0 }; mt_inspect_node(b, &ga, b->root, p); return ga.ga_data; } void mt_inspect_node(MarkTree *b, garray_T *ga, mtnode_t *n, mtpos_t off) { static char buf[1024]; ga_concat(ga, "["); if (n->level) { mt_inspect_node(b, ga, n->ptr[0], off); } for (int i = 0; i < n->n; i++) { mtpos_t p = n->key[i].pos; unrelative(off, &p); snprintf((char *)buf, sizeof(buf), "%d/%d", p.row, p.col); ga_concat(ga, buf); if (n->level) { mt_inspect_node(b, ga, n->ptr[i + 1], p); } else { ga_concat(ga, ","); } } ga_concat(ga, "]"); }