1 | //===- RewriteRope.cpp - Rope specialized for rewriter --------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file implements the RewriteRope class, which is a powerful string. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/Rewrite/Core/RewriteRope.h" |
14 | #include "clang/Basic/LLVM.h" |
15 | #include "llvm/Support/Casting.h" |
16 | #include <algorithm> |
17 | #include <cassert> |
18 | #include <cstring> |
19 | |
20 | using namespace clang; |
21 | |
22 | /// RewriteRope is a "strong" string class, designed to make insertions and |
23 | /// deletions in the middle of the string nearly constant time (really, they are |
24 | /// O(log N), but with a very low constant factor). |
25 | /// |
26 | /// The implementation of this datastructure is a conceptual linear sequence of |
27 | /// RopePiece elements. Each RopePiece represents a view on a separately |
28 | /// allocated and reference counted string. This means that splitting a very |
29 | /// long string can be done in constant time by splitting a RopePiece that |
30 | /// references the whole string into two rope pieces that reference each half. |
31 | /// Once split, another string can be inserted in between the two halves by |
32 | /// inserting a RopePiece in between the two others. All of this is very |
33 | /// inexpensive: it takes time proportional to the number of RopePieces, not the |
34 | /// length of the strings they represent. |
35 | /// |
36 | /// While a linear sequences of RopePieces is the conceptual model, the actual |
37 | /// implementation captures them in an adapted B+ Tree. Using a B+ tree (which |
38 | /// is a tree that keeps the values in the leaves and has where each node |
39 | /// contains a reasonable number of pointers to children/values) allows us to |
40 | /// maintain efficient operation when the RewriteRope contains a *huge* number |
41 | /// of RopePieces. The basic idea of the B+ Tree is that it allows us to find |
42 | /// the RopePiece corresponding to some offset very efficiently, and it |
43 | /// automatically balances itself on insertions of RopePieces (which can happen |
44 | /// for both insertions and erases of string ranges). |
45 | /// |
46 | /// The one wrinkle on the theory is that we don't attempt to keep the tree |
47 | /// properly balanced when erases happen. Erases of string data can both insert |
48 | /// new RopePieces (e.g. when the middle of some other rope piece is deleted, |
49 | /// which results in two rope pieces, which is just like an insert) or it can |
50 | /// reduce the number of RopePieces maintained by the B+Tree. In the case when |
51 | /// the number of RopePieces is reduced, we don't attempt to maintain the |
52 | /// standard 'invariant' that each node in the tree contains at least |
53 | /// 'WidthFactor' children/values. For our use cases, this doesn't seem to |
54 | /// matter. |
55 | /// |
56 | /// The implementation below is primarily implemented in terms of three classes: |
57 | /// RopePieceBTreeNode - Common base class for: |
58 | /// |
59 | /// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece |
60 | /// nodes. This directly represents a chunk of the string with those |
61 | /// RopePieces concatenated. |
62 | /// RopePieceBTreeInterior - An interior node in the B+ Tree, which manages |
63 | /// up to '2*WidthFactor' other nodes in the tree. |
64 | |
65 | namespace { |
66 | |
67 | //===----------------------------------------------------------------------===// |
68 | // RopePieceBTreeNode Class |
69 | //===----------------------------------------------------------------------===// |
70 | |
71 | /// RopePieceBTreeNode - Common base class of RopePieceBTreeLeaf and |
72 | /// RopePieceBTreeInterior. This provides some 'virtual' dispatching methods |
73 | /// and a flag that determines which subclass the instance is. Also |
74 | /// important, this node knows the full extend of the node, including any |
75 | /// children that it has. This allows efficient skipping over entire subtrees |
76 | /// when looking for an offset in the BTree. |
77 | class RopePieceBTreeNode { |
78 | protected: |
79 | /// WidthFactor - This controls the number of K/V slots held in the BTree: |
80 | /// how wide it is. Each level of the BTree is guaranteed to have at least |
81 | /// 'WidthFactor' elements in it (either ropepieces or children), (except |
82 | /// the root, which may have less) and may have at most 2*WidthFactor |
83 | /// elements. |
84 | enum { WidthFactor = 8 }; |
85 | |
86 | /// Size - This is the number of bytes of file this node (including any |
87 | /// potential children) covers. |
88 | unsigned Size = 0; |
89 | |
90 | /// IsLeaf - True if this is an instance of RopePieceBTreeLeaf, false if it |
91 | /// is an instance of RopePieceBTreeInterior. |
92 | bool IsLeaf; |
93 | |
94 | RopePieceBTreeNode(bool isLeaf) : IsLeaf(isLeaf) {} |
95 | ~RopePieceBTreeNode() = default; |
96 | |
97 | public: |
98 | bool isLeaf() const { return IsLeaf; } |
99 | unsigned size() const { return Size; } |
100 | |
101 | void Destroy(); |
102 | |
103 | /// split - Split the range containing the specified offset so that we are |
104 | /// guaranteed that there is a place to do an insertion at the specified |
105 | /// offset. The offset is relative, so "0" is the start of the node. |
106 | /// |
107 | /// If there is no space in this subtree for the extra piece, the extra tree |
108 | /// node is returned and must be inserted into a parent. |
109 | RopePieceBTreeNode *split(unsigned Offset); |
110 | |
111 | /// insert - Insert the specified ropepiece into this tree node at the |
112 | /// specified offset. The offset is relative, so "0" is the start of the |
113 | /// node. |
114 | /// |
115 | /// If there is no space in this subtree for the extra piece, the extra tree |
116 | /// node is returned and must be inserted into a parent. |
117 | RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R); |
118 | |
119 | /// erase - Remove NumBytes from this node at the specified offset. We are |
120 | /// guaranteed that there is a split at Offset. |
121 | void erase(unsigned Offset, unsigned NumBytes); |
122 | }; |
123 | |
124 | //===----------------------------------------------------------------------===// |
125 | // RopePieceBTreeLeaf Class |
126 | //===----------------------------------------------------------------------===// |
127 | |
128 | /// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece |
129 | /// nodes. This directly represents a chunk of the string with those |
130 | /// RopePieces concatenated. Since this is a B+Tree, all values (in this case |
131 | /// instances of RopePiece) are stored in leaves like this. To make iteration |
132 | /// over the leaves efficient, they maintain a singly linked list through the |
133 | /// NextLeaf field. This allows the B+Tree forward iterator to be constant |
134 | /// time for all increments. |
135 | class RopePieceBTreeLeaf : public RopePieceBTreeNode { |
136 | /// NumPieces - This holds the number of rope pieces currently active in the |
137 | /// Pieces array. |
138 | unsigned char NumPieces = 0; |
139 | |
140 | /// Pieces - This tracks the file chunks currently in this leaf. |
141 | RopePiece Pieces[2*WidthFactor]; |
142 | |
143 | /// NextLeaf - This is a pointer to the next leaf in the tree, allowing |
144 | /// efficient in-order forward iteration of the tree without traversal. |
145 | RopePieceBTreeLeaf **PrevLeaf = nullptr; |
146 | RopePieceBTreeLeaf *NextLeaf = nullptr; |
147 | |
148 | public: |
149 | RopePieceBTreeLeaf() : RopePieceBTreeNode(true) {} |
150 | |
151 | ~RopePieceBTreeLeaf() { |
152 | if (PrevLeaf || NextLeaf) |
153 | removeFromLeafInOrder(); |
154 | clear(); |
155 | } |
156 | |
157 | bool isFull() const { return NumPieces == 2*WidthFactor; } |
158 | |
159 | /// clear - Remove all rope pieces from this leaf. |
160 | void clear() { |
161 | while (NumPieces) |
162 | Pieces[--NumPieces] = RopePiece(); |
163 | Size = 0; |
164 | } |
165 | |
166 | unsigned getNumPieces() const { return NumPieces; } |
167 | |
168 | const RopePiece &getPiece(unsigned i) const { |
169 | assert(i < getNumPieces() && "Invalid piece ID" ); |
170 | return Pieces[i]; |
171 | } |
172 | |
173 | const RopePieceBTreeLeaf *getNextLeafInOrder() const { return NextLeaf; } |
174 | |
175 | void insertAfterLeafInOrder(RopePieceBTreeLeaf *Node) { |
176 | assert(!PrevLeaf && !NextLeaf && "Already in ordering" ); |
177 | |
178 | NextLeaf = Node->NextLeaf; |
179 | if (NextLeaf) |
180 | NextLeaf->PrevLeaf = &NextLeaf; |
181 | PrevLeaf = &Node->NextLeaf; |
182 | Node->NextLeaf = this; |
183 | } |
184 | |
185 | void removeFromLeafInOrder() { |
186 | if (PrevLeaf) { |
187 | *PrevLeaf = NextLeaf; |
188 | if (NextLeaf) |
189 | NextLeaf->PrevLeaf = PrevLeaf; |
190 | } else if (NextLeaf) { |
191 | NextLeaf->PrevLeaf = nullptr; |
192 | } |
193 | } |
194 | |
195 | /// FullRecomputeSizeLocally - This method recomputes the 'Size' field by |
196 | /// summing the size of all RopePieces. |
197 | void FullRecomputeSizeLocally() { |
198 | Size = 0; |
199 | for (unsigned i = 0, e = getNumPieces(); i != e; ++i) |
200 | Size += getPiece(i).size(); |
201 | } |
202 | |
203 | /// split - Split the range containing the specified offset so that we are |
204 | /// guaranteed that there is a place to do an insertion at the specified |
205 | /// offset. The offset is relative, so "0" is the start of the node. |
206 | /// |
207 | /// If there is no space in this subtree for the extra piece, the extra tree |
208 | /// node is returned and must be inserted into a parent. |
209 | RopePieceBTreeNode *split(unsigned Offset); |
210 | |
211 | /// insert - Insert the specified ropepiece into this tree node at the |
212 | /// specified offset. The offset is relative, so "0" is the start of the |
213 | /// node. |
214 | /// |
215 | /// If there is no space in this subtree for the extra piece, the extra tree |
216 | /// node is returned and must be inserted into a parent. |
217 | RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R); |
218 | |
219 | /// erase - Remove NumBytes from this node at the specified offset. We are |
220 | /// guaranteed that there is a split at Offset. |
221 | void erase(unsigned Offset, unsigned NumBytes); |
222 | |
223 | static bool classof(const RopePieceBTreeNode *N) { |
224 | return N->isLeaf(); |
225 | } |
226 | }; |
227 | |
228 | } // namespace |
229 | |
230 | /// split - Split the range containing the specified offset so that we are |
231 | /// guaranteed that there is a place to do an insertion at the specified |
232 | /// offset. The offset is relative, so "0" is the start of the node. |
233 | /// |
234 | /// If there is no space in this subtree for the extra piece, the extra tree |
235 | /// node is returned and must be inserted into a parent. |
236 | RopePieceBTreeNode *RopePieceBTreeLeaf::split(unsigned Offset) { |
237 | // Find the insertion point. We are guaranteed that there is a split at the |
238 | // specified offset so find it. |
239 | if (Offset == 0 || Offset == size()) { |
240 | // Fastpath for a common case. There is already a splitpoint at the end. |
241 | return nullptr; |
242 | } |
243 | |
244 | // Find the piece that this offset lands in. |
245 | unsigned PieceOffs = 0; |
246 | unsigned i = 0; |
247 | while (Offset >= PieceOffs+Pieces[i].size()) { |
248 | PieceOffs += Pieces[i].size(); |
249 | ++i; |
250 | } |
251 | |
252 | // If there is already a split point at the specified offset, just return |
253 | // success. |
254 | if (PieceOffs == Offset) |
255 | return nullptr; |
256 | |
257 | // Otherwise, we need to split piece 'i' at Offset-PieceOffs. Convert Offset |
258 | // to being Piece relative. |
259 | unsigned IntraPieceOffset = Offset-PieceOffs; |
260 | |
261 | // We do this by shrinking the RopePiece and then doing an insert of the tail. |
262 | RopePiece Tail(Pieces[i].StrData, Pieces[i].StartOffs+IntraPieceOffset, |
263 | Pieces[i].EndOffs); |
264 | Size -= Pieces[i].size(); |
265 | Pieces[i].EndOffs = Pieces[i].StartOffs+IntraPieceOffset; |
266 | Size += Pieces[i].size(); |
267 | |
268 | return insert(Offset, R: Tail); |
269 | } |
270 | |
271 | /// insert - Insert the specified RopePiece into this tree node at the |
272 | /// specified offset. The offset is relative, so "0" is the start of the node. |
273 | /// |
274 | /// If there is no space in this subtree for the extra piece, the extra tree |
275 | /// node is returned and must be inserted into a parent. |
276 | RopePieceBTreeNode *RopePieceBTreeLeaf::insert(unsigned Offset, |
277 | const RopePiece &R) { |
278 | // If this node is not full, insert the piece. |
279 | if (!isFull()) { |
280 | // Find the insertion point. We are guaranteed that there is a split at the |
281 | // specified offset so find it. |
282 | unsigned i = 0, e = getNumPieces(); |
283 | if (Offset == size()) { |
284 | // Fastpath for a common case. |
285 | i = e; |
286 | } else { |
287 | unsigned SlotOffs = 0; |
288 | for (; Offset > SlotOffs; ++i) |
289 | SlotOffs += getPiece(i).size(); |
290 | assert(SlotOffs == Offset && "Split didn't occur before insertion!" ); |
291 | } |
292 | |
293 | // For an insertion into a non-full leaf node, just insert the value in |
294 | // its sorted position. This requires moving later values over. |
295 | for (; i != e; --e) |
296 | Pieces[e] = Pieces[e-1]; |
297 | Pieces[i] = R; |
298 | ++NumPieces; |
299 | Size += R.size(); |
300 | return nullptr; |
301 | } |
302 | |
303 | // Otherwise, if this is leaf is full, split it in two halves. Since this |
304 | // node is full, it contains 2*WidthFactor values. We move the first |
305 | // 'WidthFactor' values to the LHS child (which we leave in this node) and |
306 | // move the last 'WidthFactor' values into the RHS child. |
307 | |
308 | // Create the new node. |
309 | RopePieceBTreeLeaf *NewNode = new RopePieceBTreeLeaf(); |
310 | |
311 | // Move over the last 'WidthFactor' values from here to NewNode. |
312 | std::copy(&Pieces[WidthFactor], &Pieces[2*WidthFactor], |
313 | &NewNode->Pieces[0]); |
314 | // Replace old pieces with null RopePieces to drop refcounts. |
315 | std::fill(&Pieces[WidthFactor], &Pieces[2*WidthFactor], RopePiece()); |
316 | |
317 | // Decrease the number of values in the two nodes. |
318 | NewNode->NumPieces = NumPieces = WidthFactor; |
319 | |
320 | // Recompute the two nodes' size. |
321 | NewNode->FullRecomputeSizeLocally(); |
322 | FullRecomputeSizeLocally(); |
323 | |
324 | // Update the list of leaves. |
325 | NewNode->insertAfterLeafInOrder(Node: this); |
326 | |
327 | // These insertions can't fail. |
328 | if (this->size() >= Offset) |
329 | this->insert(Offset, R); |
330 | else |
331 | NewNode->insert(Offset: Offset - this->size(), R); |
332 | return NewNode; |
333 | } |
334 | |
335 | /// erase - Remove NumBytes from this node at the specified offset. We are |
336 | /// guaranteed that there is a split at Offset. |
337 | void RopePieceBTreeLeaf::erase(unsigned Offset, unsigned NumBytes) { |
338 | // Since we are guaranteed that there is a split at Offset, we start by |
339 | // finding the Piece that starts there. |
340 | unsigned PieceOffs = 0; |
341 | unsigned i = 0; |
342 | for (; Offset > PieceOffs; ++i) |
343 | PieceOffs += getPiece(i).size(); |
344 | assert(PieceOffs == Offset && "Split didn't occur before erase!" ); |
345 | |
346 | unsigned StartPiece = i; |
347 | |
348 | // Figure out how many pieces completely cover 'NumBytes'. We want to remove |
349 | // all of them. |
350 | for (; Offset+NumBytes > PieceOffs+getPiece(i).size(); ++i) |
351 | PieceOffs += getPiece(i).size(); |
352 | |
353 | // If we exactly include the last one, include it in the region to delete. |
354 | if (Offset+NumBytes == PieceOffs+getPiece(i).size()) { |
355 | PieceOffs += getPiece(i).size(); |
356 | ++i; |
357 | } |
358 | |
359 | // If we completely cover some RopePieces, erase them now. |
360 | if (i != StartPiece) { |
361 | unsigned NumDeleted = i-StartPiece; |
362 | for (; i != getNumPieces(); ++i) |
363 | Pieces[i-NumDeleted] = Pieces[i]; |
364 | |
365 | // Drop references to dead rope pieces. |
366 | std::fill(&Pieces[getNumPieces()-NumDeleted], &Pieces[getNumPieces()], |
367 | RopePiece()); |
368 | NumPieces -= NumDeleted; |
369 | |
370 | unsigned CoverBytes = PieceOffs-Offset; |
371 | NumBytes -= CoverBytes; |
372 | Size -= CoverBytes; |
373 | } |
374 | |
375 | // If we completely removed some stuff, we could be done. |
376 | if (NumBytes == 0) return; |
377 | |
378 | // Okay, now might be erasing part of some Piece. If this is the case, then |
379 | // move the start point of the piece. |
380 | assert(getPiece(StartPiece).size() > NumBytes); |
381 | Pieces[StartPiece].StartOffs += NumBytes; |
382 | |
383 | // The size of this node just shrunk by NumBytes. |
384 | Size -= NumBytes; |
385 | } |
386 | |
387 | //===----------------------------------------------------------------------===// |
388 | // RopePieceBTreeInterior Class |
389 | //===----------------------------------------------------------------------===// |
390 | |
391 | namespace { |
392 | |
393 | /// RopePieceBTreeInterior - This represents an interior node in the B+Tree, |
394 | /// which holds up to 2*WidthFactor pointers to child nodes. |
395 | class RopePieceBTreeInterior : public RopePieceBTreeNode { |
396 | /// NumChildren - This holds the number of children currently active in the |
397 | /// Children array. |
398 | unsigned char NumChildren = 0; |
399 | |
400 | RopePieceBTreeNode *Children[2*WidthFactor]; |
401 | |
402 | public: |
403 | RopePieceBTreeInterior() : RopePieceBTreeNode(false) {} |
404 | |
405 | RopePieceBTreeInterior(RopePieceBTreeNode *LHS, RopePieceBTreeNode *RHS) |
406 | : RopePieceBTreeNode(false) { |
407 | Children[0] = LHS; |
408 | Children[1] = RHS; |
409 | NumChildren = 2; |
410 | Size = LHS->size() + RHS->size(); |
411 | } |
412 | |
413 | ~RopePieceBTreeInterior() { |
414 | for (unsigned i = 0, e = getNumChildren(); i != e; ++i) |
415 | Children[i]->Destroy(); |
416 | } |
417 | |
418 | bool isFull() const { return NumChildren == 2*WidthFactor; } |
419 | |
420 | unsigned getNumChildren() const { return NumChildren; } |
421 | |
422 | const RopePieceBTreeNode *getChild(unsigned i) const { |
423 | assert(i < NumChildren && "invalid child #" ); |
424 | return Children[i]; |
425 | } |
426 | |
427 | RopePieceBTreeNode *getChild(unsigned i) { |
428 | assert(i < NumChildren && "invalid child #" ); |
429 | return Children[i]; |
430 | } |
431 | |
432 | /// FullRecomputeSizeLocally - Recompute the Size field of this node by |
433 | /// summing up the sizes of the child nodes. |
434 | void FullRecomputeSizeLocally() { |
435 | Size = 0; |
436 | for (unsigned i = 0, e = getNumChildren(); i != e; ++i) |
437 | Size += getChild(i)->size(); |
438 | } |
439 | |
440 | /// split - Split the range containing the specified offset so that we are |
441 | /// guaranteed that there is a place to do an insertion at the specified |
442 | /// offset. The offset is relative, so "0" is the start of the node. |
443 | /// |
444 | /// If there is no space in this subtree for the extra piece, the extra tree |
445 | /// node is returned and must be inserted into a parent. |
446 | RopePieceBTreeNode *split(unsigned Offset); |
447 | |
448 | /// insert - Insert the specified ropepiece into this tree node at the |
449 | /// specified offset. The offset is relative, so "0" is the start of the |
450 | /// node. |
451 | /// |
452 | /// If there is no space in this subtree for the extra piece, the extra tree |
453 | /// node is returned and must be inserted into a parent. |
454 | RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R); |
455 | |
456 | /// HandleChildPiece - A child propagated an insertion result up to us. |
457 | /// Insert the new child, and/or propagate the result further up the tree. |
458 | RopePieceBTreeNode *HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS); |
459 | |
460 | /// erase - Remove NumBytes from this node at the specified offset. We are |
461 | /// guaranteed that there is a split at Offset. |
462 | void erase(unsigned Offset, unsigned NumBytes); |
463 | |
464 | static bool classof(const RopePieceBTreeNode *N) { |
465 | return !N->isLeaf(); |
466 | } |
467 | }; |
468 | |
469 | } // namespace |
470 | |
471 | /// split - Split the range containing the specified offset so that we are |
472 | /// guaranteed that there is a place to do an insertion at the specified |
473 | /// offset. The offset is relative, so "0" is the start of the node. |
474 | /// |
475 | /// If there is no space in this subtree for the extra piece, the extra tree |
476 | /// node is returned and must be inserted into a parent. |
477 | RopePieceBTreeNode *RopePieceBTreeInterior::split(unsigned Offset) { |
478 | // Figure out which child to split. |
479 | if (Offset == 0 || Offset == size()) |
480 | return nullptr; // If we have an exact offset, we're already split. |
481 | |
482 | unsigned ChildOffset = 0; |
483 | unsigned i = 0; |
484 | for (; Offset >= ChildOffset+getChild(i)->size(); ++i) |
485 | ChildOffset += getChild(i)->size(); |
486 | |
487 | // If already split there, we're done. |
488 | if (ChildOffset == Offset) |
489 | return nullptr; |
490 | |
491 | // Otherwise, recursively split the child. |
492 | if (RopePieceBTreeNode *RHS = getChild(i)->split(Offset: Offset-ChildOffset)) |
493 | return HandleChildPiece(i, RHS); |
494 | return nullptr; // Done! |
495 | } |
496 | |
497 | /// insert - Insert the specified ropepiece into this tree node at the |
498 | /// specified offset. The offset is relative, so "0" is the start of the |
499 | /// node. |
500 | /// |
501 | /// If there is no space in this subtree for the extra piece, the extra tree |
502 | /// node is returned and must be inserted into a parent. |
503 | RopePieceBTreeNode *RopePieceBTreeInterior::insert(unsigned Offset, |
504 | const RopePiece &R) { |
505 | // Find the insertion point. We are guaranteed that there is a split at the |
506 | // specified offset so find it. |
507 | unsigned i = 0, e = getNumChildren(); |
508 | |
509 | unsigned ChildOffs = 0; |
510 | if (Offset == size()) { |
511 | // Fastpath for a common case. Insert at end of last child. |
512 | i = e-1; |
513 | ChildOffs = size()-getChild(i)->size(); |
514 | } else { |
515 | for (; Offset > ChildOffs+getChild(i)->size(); ++i) |
516 | ChildOffs += getChild(i)->size(); |
517 | } |
518 | |
519 | Size += R.size(); |
520 | |
521 | // Insert at the end of this child. |
522 | if (RopePieceBTreeNode *RHS = getChild(i)->insert(Offset: Offset-ChildOffs, R)) |
523 | return HandleChildPiece(i, RHS); |
524 | |
525 | return nullptr; |
526 | } |
527 | |
528 | /// HandleChildPiece - A child propagated an insertion result up to us. |
529 | /// Insert the new child, and/or propagate the result further up the tree. |
530 | RopePieceBTreeNode * |
531 | RopePieceBTreeInterior::HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS) { |
532 | // Otherwise the child propagated a subtree up to us as a new child. See if |
533 | // we have space for it here. |
534 | if (!isFull()) { |
535 | // Insert RHS after child 'i'. |
536 | if (i + 1 != getNumChildren()) |
537 | memmove(dest: &Children[i+2], src: &Children[i+1], |
538 | n: (getNumChildren()-i-1)*sizeof(Children[0])); |
539 | Children[i+1] = RHS; |
540 | ++NumChildren; |
541 | return nullptr; |
542 | } |
543 | |
544 | // Okay, this node is full. Split it in half, moving WidthFactor children to |
545 | // a newly allocated interior node. |
546 | |
547 | // Create the new node. |
548 | RopePieceBTreeInterior *NewNode = new RopePieceBTreeInterior(); |
549 | |
550 | // Move over the last 'WidthFactor' values from here to NewNode. |
551 | memcpy(dest: &NewNode->Children[0], src: &Children[WidthFactor], |
552 | n: WidthFactor*sizeof(Children[0])); |
553 | |
554 | // Decrease the number of values in the two nodes. |
555 | NewNode->NumChildren = NumChildren = WidthFactor; |
556 | |
557 | // Finally, insert the two new children in the side the can (now) hold them. |
558 | // These insertions can't fail. |
559 | if (i < WidthFactor) |
560 | this->HandleChildPiece(i, RHS); |
561 | else |
562 | NewNode->HandleChildPiece(i: i-WidthFactor, RHS); |
563 | |
564 | // Recompute the two nodes' size. |
565 | NewNode->FullRecomputeSizeLocally(); |
566 | FullRecomputeSizeLocally(); |
567 | return NewNode; |
568 | } |
569 | |
570 | /// erase - Remove NumBytes from this node at the specified offset. We are |
571 | /// guaranteed that there is a split at Offset. |
572 | void RopePieceBTreeInterior::erase(unsigned Offset, unsigned NumBytes) { |
573 | // This will shrink this node by NumBytes. |
574 | Size -= NumBytes; |
575 | |
576 | // Find the first child that overlaps with Offset. |
577 | unsigned i = 0; |
578 | for (; Offset >= getChild(i)->size(); ++i) |
579 | Offset -= getChild(i)->size(); |
580 | |
581 | // Propagate the delete request into overlapping children, or completely |
582 | // delete the children as appropriate. |
583 | while (NumBytes) { |
584 | RopePieceBTreeNode *CurChild = getChild(i); |
585 | |
586 | // If we are deleting something contained entirely in the child, pass on the |
587 | // request. |
588 | if (Offset+NumBytes < CurChild->size()) { |
589 | CurChild->erase(Offset, NumBytes); |
590 | return; |
591 | } |
592 | |
593 | // If this deletion request starts somewhere in the middle of the child, it |
594 | // must be deleting to the end of the child. |
595 | if (Offset) { |
596 | unsigned BytesFromChild = CurChild->size()-Offset; |
597 | CurChild->erase(Offset, NumBytes: BytesFromChild); |
598 | NumBytes -= BytesFromChild; |
599 | // Start at the beginning of the next child. |
600 | Offset = 0; |
601 | ++i; |
602 | continue; |
603 | } |
604 | |
605 | // If the deletion request completely covers the child, delete it and move |
606 | // the rest down. |
607 | NumBytes -= CurChild->size(); |
608 | CurChild->Destroy(); |
609 | --NumChildren; |
610 | if (i != getNumChildren()) |
611 | memmove(dest: &Children[i], src: &Children[i+1], |
612 | n: (getNumChildren()-i)*sizeof(Children[0])); |
613 | } |
614 | } |
615 | |
616 | //===----------------------------------------------------------------------===// |
617 | // RopePieceBTreeNode Implementation |
618 | //===----------------------------------------------------------------------===// |
619 | |
620 | void RopePieceBTreeNode::Destroy() { |
621 | if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(Val: this)) |
622 | delete Leaf; |
623 | else |
624 | delete cast<RopePieceBTreeInterior>(Val: this); |
625 | } |
626 | |
627 | /// split - Split the range containing the specified offset so that we are |
628 | /// guaranteed that there is a place to do an insertion at the specified |
629 | /// offset. The offset is relative, so "0" is the start of the node. |
630 | /// |
631 | /// If there is no space in this subtree for the extra piece, the extra tree |
632 | /// node is returned and must be inserted into a parent. |
633 | RopePieceBTreeNode *RopePieceBTreeNode::split(unsigned Offset) { |
634 | assert(Offset <= size() && "Invalid offset to split!" ); |
635 | if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(Val: this)) |
636 | return Leaf->split(Offset); |
637 | return cast<RopePieceBTreeInterior>(Val: this)->split(Offset); |
638 | } |
639 | |
640 | /// insert - Insert the specified ropepiece into this tree node at the |
641 | /// specified offset. The offset is relative, so "0" is the start of the |
642 | /// node. |
643 | /// |
644 | /// If there is no space in this subtree for the extra piece, the extra tree |
645 | /// node is returned and must be inserted into a parent. |
646 | RopePieceBTreeNode *RopePieceBTreeNode::insert(unsigned Offset, |
647 | const RopePiece &R) { |
648 | assert(Offset <= size() && "Invalid offset to insert!" ); |
649 | if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(Val: this)) |
650 | return Leaf->insert(Offset, R); |
651 | return cast<RopePieceBTreeInterior>(Val: this)->insert(Offset, R); |
652 | } |
653 | |
654 | /// erase - Remove NumBytes from this node at the specified offset. We are |
655 | /// guaranteed that there is a split at Offset. |
656 | void RopePieceBTreeNode::erase(unsigned Offset, unsigned NumBytes) { |
657 | assert(Offset+NumBytes <= size() && "Invalid offset to erase!" ); |
658 | if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(Val: this)) |
659 | return Leaf->erase(Offset, NumBytes); |
660 | return cast<RopePieceBTreeInterior>(Val: this)->erase(Offset, NumBytes); |
661 | } |
662 | |
663 | //===----------------------------------------------------------------------===// |
664 | // RopePieceBTreeIterator Implementation |
665 | //===----------------------------------------------------------------------===// |
666 | |
667 | static const RopePieceBTreeLeaf *getCN(const void *P) { |
668 | return static_cast<const RopePieceBTreeLeaf*>(P); |
669 | } |
670 | |
671 | // begin iterator. |
672 | RopePieceBTreeIterator::RopePieceBTreeIterator(const void *n) { |
673 | const auto *N = static_cast<const RopePieceBTreeNode *>(n); |
674 | |
675 | // Walk down the left side of the tree until we get to a leaf. |
676 | while (const auto *IN = dyn_cast<RopePieceBTreeInterior>(Val: N)) |
677 | N = IN->getChild(i: 0); |
678 | |
679 | // We must have at least one leaf. |
680 | CurNode = cast<RopePieceBTreeLeaf>(Val: N); |
681 | |
682 | // If we found a leaf that happens to be empty, skip over it until we get |
683 | // to something full. |
684 | while (CurNode && getCN(P: CurNode)->getNumPieces() == 0) |
685 | CurNode = getCN(P: CurNode)->getNextLeafInOrder(); |
686 | |
687 | if (CurNode) |
688 | CurPiece = &getCN(P: CurNode)->getPiece(i: 0); |
689 | else // Empty tree, this is an end() iterator. |
690 | CurPiece = nullptr; |
691 | CurChar = 0; |
692 | } |
693 | |
694 | void RopePieceBTreeIterator::MoveToNextPiece() { |
695 | if (CurPiece != &getCN(P: CurNode)->getPiece(i: getCN(P: CurNode)->getNumPieces()-1)) { |
696 | CurChar = 0; |
697 | ++CurPiece; |
698 | return; |
699 | } |
700 | |
701 | // Find the next non-empty leaf node. |
702 | do |
703 | CurNode = getCN(P: CurNode)->getNextLeafInOrder(); |
704 | while (CurNode && getCN(P: CurNode)->getNumPieces() == 0); |
705 | |
706 | if (CurNode) |
707 | CurPiece = &getCN(P: CurNode)->getPiece(i: 0); |
708 | else // Hit end(). |
709 | CurPiece = nullptr; |
710 | CurChar = 0; |
711 | } |
712 | |
713 | //===----------------------------------------------------------------------===// |
714 | // RopePieceBTree Implementation |
715 | //===----------------------------------------------------------------------===// |
716 | |
717 | static RopePieceBTreeNode *getRoot(void *P) { |
718 | return static_cast<RopePieceBTreeNode*>(P); |
719 | } |
720 | |
721 | RopePieceBTree::RopePieceBTree() { |
722 | Root = new RopePieceBTreeLeaf(); |
723 | } |
724 | |
725 | RopePieceBTree::RopePieceBTree(const RopePieceBTree &RHS) { |
726 | assert(RHS.empty() && "Can't copy non-empty tree yet" ); |
727 | Root = new RopePieceBTreeLeaf(); |
728 | } |
729 | |
730 | RopePieceBTree::~RopePieceBTree() { |
731 | getRoot(P: Root)->Destroy(); |
732 | } |
733 | |
734 | unsigned RopePieceBTree::size() const { |
735 | return getRoot(P: Root)->size(); |
736 | } |
737 | |
738 | void RopePieceBTree::clear() { |
739 | if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(Val: getRoot(P: Root))) |
740 | Leaf->clear(); |
741 | else { |
742 | getRoot(P: Root)->Destroy(); |
743 | Root = new RopePieceBTreeLeaf(); |
744 | } |
745 | } |
746 | |
747 | void RopePieceBTree::insert(unsigned Offset, const RopePiece &R) { |
748 | // #1. Split at Offset. |
749 | if (RopePieceBTreeNode *RHS = getRoot(P: Root)->split(Offset)) |
750 | Root = new RopePieceBTreeInterior(getRoot(P: Root), RHS); |
751 | |
752 | // #2. Do the insertion. |
753 | if (RopePieceBTreeNode *RHS = getRoot(P: Root)->insert(Offset, R)) |
754 | Root = new RopePieceBTreeInterior(getRoot(P: Root), RHS); |
755 | } |
756 | |
757 | void RopePieceBTree::erase(unsigned Offset, unsigned NumBytes) { |
758 | // #1. Split at Offset. |
759 | if (RopePieceBTreeNode *RHS = getRoot(P: Root)->split(Offset)) |
760 | Root = new RopePieceBTreeInterior(getRoot(P: Root), RHS); |
761 | |
762 | // #2. Do the erasing. |
763 | getRoot(P: Root)->erase(Offset, NumBytes); |
764 | } |
765 | |
766 | //===----------------------------------------------------------------------===// |
767 | // RewriteRope Implementation |
768 | //===----------------------------------------------------------------------===// |
769 | |
770 | /// MakeRopeString - This copies the specified byte range into some instance of |
771 | /// RopeRefCountString, and return a RopePiece that represents it. This uses |
772 | /// the AllocBuffer object to aggregate requests for small strings into one |
773 | /// allocation instead of doing tons of tiny allocations. |
774 | RopePiece RewriteRope::MakeRopeString(const char *Start, const char *End) { |
775 | unsigned Len = End-Start; |
776 | assert(Len && "Zero length RopePiece is invalid!" ); |
777 | |
778 | // If we have space for this string in the current alloc buffer, use it. |
779 | if (AllocOffs+Len <= AllocChunkSize) { |
780 | memcpy(dest: AllocBuffer->Data+AllocOffs, src: Start, n: Len); |
781 | AllocOffs += Len; |
782 | return RopePiece(AllocBuffer, AllocOffs-Len, AllocOffs); |
783 | } |
784 | |
785 | // If we don't have enough room because this specific allocation is huge, |
786 | // just allocate a new rope piece for it alone. |
787 | if (Len > AllocChunkSize) { |
788 | unsigned Size = End-Start+sizeof(RopeRefCountString)-1; |
789 | auto *Res = reinterpret_cast<RopeRefCountString *>(new char[Size]); |
790 | Res->RefCount = 0; |
791 | memcpy(dest: Res->Data, src: Start, n: End-Start); |
792 | return RopePiece(Res, 0, End-Start); |
793 | } |
794 | |
795 | // Otherwise, this was a small request but we just don't have space for it |
796 | // Make a new chunk and share it with later allocations. |
797 | |
798 | unsigned AllocSize = offsetof(RopeRefCountString, Data) + AllocChunkSize; |
799 | auto *Res = reinterpret_cast<RopeRefCountString *>(new char[AllocSize]); |
800 | Res->RefCount = 0; |
801 | memcpy(dest: Res->Data, src: Start, n: Len); |
802 | AllocBuffer = Res; |
803 | AllocOffs = Len; |
804 | |
805 | return RopePiece(AllocBuffer, 0, Len); |
806 | } |
807 | |