Path: blob/main/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Piece, PieceTreeBase } from './pieceTreeBase.js';67export class TreeNode {8parent: TreeNode;9left: TreeNode;10right: TreeNode;11color: NodeColor;1213// Piece14piece: Piece;15size_left: number; // size of the left subtree (not inorder)16lf_left: number; // line feeds cnt in the left subtree (not in order)1718constructor(piece: Piece, color: NodeColor) {19this.piece = piece;20this.color = color;21this.size_left = 0;22this.lf_left = 0;23this.parent = this;24this.left = this;25this.right = this;26}2728public next(): TreeNode {29if (this.right !== SENTINEL) {30return leftest(this.right);31}3233let node: TreeNode = this;3435while (node.parent !== SENTINEL) {36if (node.parent.left === node) {37break;38}3940node = node.parent;41}4243if (node.parent === SENTINEL) {44return SENTINEL;45} else {46return node.parent;47}48}4950public prev(): TreeNode {51if (this.left !== SENTINEL) {52return righttest(this.left);53}5455let node: TreeNode = this;5657while (node.parent !== SENTINEL) {58if (node.parent.right === node) {59break;60}6162node = node.parent;63}6465if (node.parent === SENTINEL) {66return SENTINEL;67} else {68return node.parent;69}70}7172public detach(): void {73this.parent = null!;74this.left = null!;75this.right = null!;76}77}7879export const enum NodeColor {80Black = 0,81Red = 1,82}8384export const SENTINEL: TreeNode = new TreeNode(null!, NodeColor.Black);85SENTINEL.parent = SENTINEL;86SENTINEL.left = SENTINEL;87SENTINEL.right = SENTINEL;88SENTINEL.color = NodeColor.Black;8990export function leftest(node: TreeNode): TreeNode {91while (node.left !== SENTINEL) {92node = node.left;93}94return node;95}9697export function righttest(node: TreeNode): TreeNode {98while (node.right !== SENTINEL) {99node = node.right;100}101return node;102}103104function calculateSize(node: TreeNode): number {105if (node === SENTINEL) {106return 0;107}108109return node.size_left + node.piece.length + calculateSize(node.right);110}111112function calculateLF(node: TreeNode): number {113if (node === SENTINEL) {114return 0;115}116117return node.lf_left + node.piece.lineFeedCnt + calculateLF(node.right);118}119120function resetSentinel(): void {121SENTINEL.parent = SENTINEL;122}123124export function leftRotate(tree: PieceTreeBase, x: TreeNode) {125const y = x.right;126127// fix size_left128y.size_left += x.size_left + (x.piece ? x.piece.length : 0);129y.lf_left += x.lf_left + (x.piece ? x.piece.lineFeedCnt : 0);130x.right = y.left;131132if (y.left !== SENTINEL) {133y.left.parent = x;134}135y.parent = x.parent;136if (x.parent === SENTINEL) {137tree.root = y;138} else if (x.parent.left === x) {139x.parent.left = y;140} else {141x.parent.right = y;142}143y.left = x;144x.parent = y;145}146147export function rightRotate(tree: PieceTreeBase, y: TreeNode) {148const x = y.left;149y.left = x.right;150if (x.right !== SENTINEL) {151x.right.parent = y;152}153x.parent = y.parent;154155// fix size_left156y.size_left -= x.size_left + (x.piece ? x.piece.length : 0);157y.lf_left -= x.lf_left + (x.piece ? x.piece.lineFeedCnt : 0);158159if (y.parent === SENTINEL) {160tree.root = x;161} else if (y === y.parent.right) {162y.parent.right = x;163} else {164y.parent.left = x;165}166167x.right = y;168y.parent = x;169}170171export function rbDelete(tree: PieceTreeBase, z: TreeNode) {172let x: TreeNode;173let y: TreeNode;174175if (z.left === SENTINEL) {176y = z;177x = y.right;178} else if (z.right === SENTINEL) {179y = z;180x = y.left;181} else {182y = leftest(z.right);183x = y.right;184}185186if (y === tree.root) {187tree.root = x;188189// if x is null, we are removing the only node190x.color = NodeColor.Black;191z.detach();192resetSentinel();193tree.root.parent = SENTINEL;194195return;196}197198const yWasRed = (y.color === NodeColor.Red);199200if (y === y.parent.left) {201y.parent.left = x;202} else {203y.parent.right = x;204}205206if (y === z) {207x.parent = y.parent;208recomputeTreeMetadata(tree, x);209} else {210if (y.parent === z) {211x.parent = y;212} else {213x.parent = y.parent;214}215216// as we make changes to x's hierarchy, update size_left of subtree first217recomputeTreeMetadata(tree, x);218219y.left = z.left;220y.right = z.right;221y.parent = z.parent;222y.color = z.color;223224if (z === tree.root) {225tree.root = y;226} else {227if (z === z.parent.left) {228z.parent.left = y;229} else {230z.parent.right = y;231}232}233234if (y.left !== SENTINEL) {235y.left.parent = y;236}237if (y.right !== SENTINEL) {238y.right.parent = y;239}240// update metadata241// we replace z with y, so in this sub tree, the length change is z.item.length242y.size_left = z.size_left;243y.lf_left = z.lf_left;244recomputeTreeMetadata(tree, y);245}246247z.detach();248249if (x.parent.left === x) {250const newSizeLeft = calculateSize(x);251const newLFLeft = calculateLF(x);252if (newSizeLeft !== x.parent.size_left || newLFLeft !== x.parent.lf_left) {253const delta = newSizeLeft - x.parent.size_left;254const lf_delta = newLFLeft - x.parent.lf_left;255x.parent.size_left = newSizeLeft;256x.parent.lf_left = newLFLeft;257updateTreeMetadata(tree, x.parent, delta, lf_delta);258}259}260261recomputeTreeMetadata(tree, x.parent);262263if (yWasRed) {264resetSentinel();265return;266}267268// RB-DELETE-FIXUP269let w: TreeNode;270while (x !== tree.root && x.color === NodeColor.Black) {271if (x === x.parent.left) {272w = x.parent.right;273274if (w.color === NodeColor.Red) {275w.color = NodeColor.Black;276x.parent.color = NodeColor.Red;277leftRotate(tree, x.parent);278w = x.parent.right;279}280281if (w.left.color === NodeColor.Black && w.right.color === NodeColor.Black) {282w.color = NodeColor.Red;283x = x.parent;284} else {285if (w.right.color === NodeColor.Black) {286w.left.color = NodeColor.Black;287w.color = NodeColor.Red;288rightRotate(tree, w);289w = x.parent.right;290}291292w.color = x.parent.color;293x.parent.color = NodeColor.Black;294w.right.color = NodeColor.Black;295leftRotate(tree, x.parent);296x = tree.root;297}298} else {299w = x.parent.left;300301if (w.color === NodeColor.Red) {302w.color = NodeColor.Black;303x.parent.color = NodeColor.Red;304rightRotate(tree, x.parent);305w = x.parent.left;306}307308if (w.left.color === NodeColor.Black && w.right.color === NodeColor.Black) {309w.color = NodeColor.Red;310x = x.parent;311312} else {313if (w.left.color === NodeColor.Black) {314w.right.color = NodeColor.Black;315w.color = NodeColor.Red;316leftRotate(tree, w);317w = x.parent.left;318}319320w.color = x.parent.color;321x.parent.color = NodeColor.Black;322w.left.color = NodeColor.Black;323rightRotate(tree, x.parent);324x = tree.root;325}326}327}328x.color = NodeColor.Black;329resetSentinel();330}331332export function fixInsert(tree: PieceTreeBase, x: TreeNode) {333recomputeTreeMetadata(tree, x);334335while (x !== tree.root && x.parent.color === NodeColor.Red) {336if (x.parent === x.parent.parent.left) {337const y = x.parent.parent.right;338339if (y.color === NodeColor.Red) {340x.parent.color = NodeColor.Black;341y.color = NodeColor.Black;342x.parent.parent.color = NodeColor.Red;343x = x.parent.parent;344} else {345if (x === x.parent.right) {346x = x.parent;347leftRotate(tree, x);348}349350x.parent.color = NodeColor.Black;351x.parent.parent.color = NodeColor.Red;352rightRotate(tree, x.parent.parent);353}354} else {355const y = x.parent.parent.left;356357if (y.color === NodeColor.Red) {358x.parent.color = NodeColor.Black;359y.color = NodeColor.Black;360x.parent.parent.color = NodeColor.Red;361x = x.parent.parent;362} else {363if (x === x.parent.left) {364x = x.parent;365rightRotate(tree, x);366}367x.parent.color = NodeColor.Black;368x.parent.parent.color = NodeColor.Red;369leftRotate(tree, x.parent.parent);370}371}372}373374tree.root.color = NodeColor.Black;375}376377export function updateTreeMetadata(tree: PieceTreeBase, x: TreeNode, delta: number, lineFeedCntDelta: number): void {378// node length change or line feed count change379while (x !== tree.root && x !== SENTINEL) {380if (x.parent.left === x) {381x.parent.size_left += delta;382x.parent.lf_left += lineFeedCntDelta;383}384385x = x.parent;386}387}388389export function recomputeTreeMetadata(tree: PieceTreeBase, x: TreeNode) {390let delta = 0;391let lf_delta = 0;392if (x === tree.root) {393return;394}395396// go upwards till the node whose left subtree is changed.397while (x !== tree.root && x === x.parent.right) {398x = x.parent;399}400401if (x === tree.root) {402// well, it means we add a node to the end (inorder)403return;404}405406// x is the node whose right subtree is changed.407x = x.parent;408409delta = calculateSize(x.left) - x.size_left;410lf_delta = calculateLF(x.left) - x.lf_left;411x.size_left += delta;412x.lf_left += lf_delta;413414415// go upwards till root. O(logN)416while (x !== tree.root && (delta !== 0 || lf_delta !== 0)) {417if (x.parent.left === x) {418x.parent.size_left += delta;419x.parent.lf_left += lf_delta;420}421422x = x.parent;423}424}425426427