Skip to main content

piece_tree/
lib.rs

1#![cfg_attr(not(feature = "write"), no_std)]
2
3#![warn(clippy::all, clippy::pedantic, dead_code, clippy::cargo)]
4#![allow(
5    unused_assignments,
6    clippy::inline_always,
7    clippy::uninlined_format_args, // ?...
8    clippy::borrow_as_ptr,
9    clippy::collapsible_if,
10    clippy::new_without_default,
11    clippy::redundant_field_names,
12    clippy::pub_underscore_fields,
13    clippy::struct_field_names,
14    clippy::ptr_as_ptr,
15    clippy::missing_transmute_annotations,
16    clippy::multiple_crate_versions,
17    clippy::cast_precision_loss,
18    clippy::cast_possible_truncation,
19    clippy::similar_names,
20    clippy::cast_sign_loss,
21    clippy::cast_possible_wrap,
22    clippy::used_underscore_binding,
23    clippy::nonstandard_macro_braces,
24    clippy::used_underscore_items,
25    clippy::enum_glob_use,
26    clippy::cast_lossless,
27    clippy::match_same_arms,
28    clippy::too_many_lines,
29    clippy::unnested_or_patterns,
30    clippy::blocks_in_conditions,
31    clippy::missing_errors_doc,
32    clippy::missing_panics_doc,
33)]
34
35#[cfg(feature = "write")]
36extern crate std;
37
38extern crate alloc;
39
40#[allow(unused)]
41use alloc::vec;
42use alloc::vec::Vec;
43use alloc::sync::Arc;
44use alloc::string::{String, ToString};
45
46use core::str;
47use core::cmp::Ordering;
48use core::ops::{Bound, Deref, RangeBounds};
49
50#[cfg(not(feature = "dont_vendor"))]
51mod cranelift_entity_vendor;
52#[cfg(not(feature = "dont_vendor"))]
53use cranelift_entity_vendor as cranelift_entity;
54
55#[cfg(not(feature = "dont_vendor"))]
56mod smallvec_vendor;
57#[cfg(not(feature = "dont_vendor"))]
58use smallvec_vendor as smallvec;
59
60#[cfg(not(feature = "dont_vendor"))]
61mod bytecount_vendor;
62#[cfg(not(feature = "dont_vendor"))]
63use bytecount_vendor as bytecount;
64
65use smallvec::SmallVec;
66use cranelift_entity::{EntityRef, PrimaryMap};
67#[cfg(feature = "dont_vendor")]
68use cranelift_entity::entity_impl;
69
70#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
71pub struct BufferRef(pub u32);
72entity_impl!(BufferRef);
73
74#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
75pub struct NodeRef(pub u32);
76entity_impl!(NodeRef);
77
78pub const NIL: NodeRef = NodeRef(0);
79
80pub const MOD_BUFFER: BufferRef = BufferRef(u32::MAX >> 1);
81
82pub const CHECKPOINT_INTERVAL: u32 = 64;
83
84pub const MAX_PIECE_SIZE: u32 = 64 * 1024;
85
86#[derive(Clone, Copy, PartialEq, Eq, Debug)]
87pub enum Color { Black = 0, Red = 1 }
88
89#[derive(Clone, Copy, Debug)]
90pub struct HistoryEntry {
91    pub root:          NodeRef,
92    pub cursor_offset: u32,
93}
94
95#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
96pub struct Piece {
97    pub buffer:            BufferRef,
98
99    pub byte_offset:       u32,
100    pub byte_length:       u32,
101
102    pub newline_count:     u32,
103    pub char_count:        u32,
104
105    pub buffer_start_line: u32,  // Index into that buffer's `newline_offsets` array of this piece's first '\n'
106
107    pub piece_start_char:  u32,  // Absolute char index of p.offset in its buffer
108}
109
110// For tests and other stuff
111#[derive(Clone, Copy, Debug)]
112pub enum Edit {
113    Insert { offset: u32, text: &'static str },
114    Remove { offset: u32, length: u32 },
115}
116
117impl Edit {
118    #[inline(always)]
119    #[must_use]
120    pub fn offset(&self) -> u32 {
121        match self {
122            Edit::Insert { offset, .. } => *offset,
123            Edit::Remove { offset, .. } => *offset,
124        }
125    }
126}
127
128#[derive(Clone, Copy, Debug)]
129#[repr(C)]
130pub struct Node {
131    pub left:              NodeRef, // 4 bytes
132    pub right:             NodeRef, // 4 bytes
133    pub offset:            u32,     // 4 bytes
134    pub subtree_len:       u32,     // 4 bytes
135    pub subtree_chars:     u32,     // 4 bytes
136    pub subtree_newlines:  u32,     // 4 bytes
137    pub meta:              u32,     // 4 bytes (Bit 0: Color, Bits 1..31: BufferIndex)
138    pub buffer_start_line: u32,     // 4 bytes
139    pub piece_start_char:  u32,     // 4 bytes
140}
141
142const _: () = assert!(size_of::<Node>() == 36);
143
144impl Node {
145    #[inline(always)]
146    #[must_use]
147    pub fn color(&self) -> Color {
148        if (self.meta & 1) == 1 { Color::Red } else { Color::Black }
149    }
150
151    #[inline(always)]
152    pub fn set_color(&mut self, color: Color) {
153        self.meta = (self.meta & !1) | (color as u32);
154    }
155
156    #[inline(always)]
157    #[must_use]
158    pub fn buffer_index(&self) -> BufferRef {
159        BufferRef(self.meta >> 1)
160    }
161
162    #[inline(always)]
163    pub fn set_buffer(&mut self, buffer: BufferRef) {
164        self.meta = (buffer.as_u32() << 1) | (self.meta & 1);
165    }
166}
167
168#[derive(Debug, Clone)]
169pub struct OriginalBuffer {
170    pub text:             Arc<str>,
171    pub newline_offsets:  Arc<[u32]>,
172    pub char_checkpoints: Arc<[(u32, u32)]>,
173}
174
175impl Deref for OriginalBuffer {
176    type Target = str;
177
178    #[inline(always)]
179    fn deref(&self) -> &Self::Target {
180        self.text.as_ref()
181    }
182}
183
184#[derive(Debug, Clone)]
185pub struct Buffers {
186    pub original_buffers: PrimaryMap<BufferRef, OriginalBuffer>,
187
188    pub modifications_buffer:           String,
189    pub modifications_newline_offsets:  Vec<u32>,
190    pub modifications_char_checkpoints: Vec<(u32, u32)>,
191}
192
193impl Default for Buffers {
194    #[inline(always)]
195    fn default() -> Self {
196        Self {
197            original_buffers: PrimaryMap::new(),
198            modifications_newline_offsets: Vec::with_capacity(1024),
199            modifications_char_checkpoints: Vec::with_capacity(128),
200            modifications_buffer: String::with_capacity(1024 * 64),
201        }
202    }
203}
204
205#[must_use]
206#[inline(always)]
207pub fn count_chars_and_newlines(bytes: &[u8]) -> (u32, u32) {
208    let mut chars    = 0u32;
209    let mut newlines = 0u32;
210
211    for &b in bytes {
212        newlines += (b == b'\n')         as u32;
213        chars    += ((b & 0xC0) != 0x80) as u32;
214    }
215
216    (chars, newlines)
217}
218
219#[must_use]
220#[inline(always)]
221pub fn count_chars_and_newlines_with_offsets_and_checkpoints(
222    bytes: &[u8],
223    offsets: &mut Vec<u32>,
224    checkpoints: &mut Vec<(u32, u32)>,
225) -> (u32, u32) {
226    let mut chars    = 0u32;
227    let mut newlines = 0u32;
228
229    let mut next_checkpoint = CHECKPOINT_INTERVAL;
230
231    for (i, &b) in bytes.iter().enumerate() {
232        if (b as i8) >= -0x40 {
233            if chars == next_checkpoint {
234                checkpoints.push((chars, i as u32));
235                next_checkpoint += CHECKPOINT_INTERVAL;
236            }
237            chars += 1;
238        }
239
240        if b == b'\n' {
241            offsets.push(i as u32);
242            newlines += 1;
243        }
244    }
245
246    (chars, newlines)
247}
248
249impl Buffers {
250    #[inline(always)]
251    #[must_use]
252    pub fn new() -> Self { Self::default() }
253
254    #[inline(always)]
255    #[must_use]
256    pub fn get(&self, buffer: BufferRef) -> &str {
257        if buffer == MOD_BUFFER {
258            &self.modifications_buffer
259        } else {
260            &self.original_buffers[buffer]
261        }
262    }
263
264    #[inline(always)]
265    #[must_use]
266    pub fn get_slice(&self, buffer: BufferRef, offset: u32, len: u32) -> &str {
267        let buf = self.get(buffer);
268        let start = offset as usize;
269        let end = start + len as usize;
270        unsafe { str::from_utf8_unchecked(buf.as_bytes().get_unchecked(start..end)) }
271    }
272
273    #[inline]
274    pub fn count_chars_and_newlines(&self, buffer: BufferRef, start_offset: u32, len: u32) -> (u32, u32) {
275        if len == 0 { return (0, 0) }
276
277        let end_offset = start_offset + len;
278
279        //
280        // O(log N) newlines count
281        //
282        let nl_offsets = self.get_newlines(buffer);
283        let start_nl_index = nl_offsets.binary_search(&start_offset).unwrap_or_else(|x| x);
284        let end_nl_index = nl_offsets.binary_search(&end_offset).unwrap_or_else(|x| x);
285        let newlines = (end_nl_index - start_nl_index) as u32;
286
287        //
288        // O(log N) char count
289        //
290
291        let checkpoints = self.get_checkpoints(buffer);
292        let buffer_text = self.get(buffer);
293
294        //
295        // Gets absolute char count from start of buffer to `target_offset`
296        //
297        let chars_up_to = |target_offset: u32| -> u32 {
298            if target_offset == 0 { return 0; }
299
300            //
301            // Find the index of the first checkpoint that is strictly strictly greater than our target
302            //
303            let partition_index = checkpoints.partition_point(|&(_char_cnt, byte_offset)| byte_offset <= target_offset);
304
305            let (base_chars, base_bytes) = if partition_index > 0 {
306                checkpoints[partition_index - 1]
307            } else {
308                (0, 0)
309            };
310
311            //
312            // Linearly scan only the remaining bytes from the checkpoint to the target
313            //
314            let tail_bytes = &buffer_text[base_bytes as usize .. target_offset as usize];
315            base_chars + bytecount::num_chars(tail_bytes.as_bytes()) as u32
316        };
317
318        let chars = chars_up_to(end_offset) - chars_up_to(start_offset);
319
320        (chars, newlines)
321    }
322
323    #[inline(always)]
324    #[must_use]
325    pub fn count_chars(&self, buffer: BufferRef, offset: u32, len: u32) -> u32 {
326        bytecount::num_chars(self.get_slice(buffer, offset, len).as_bytes()) as u32
327    }
328
329    #[inline(always)]
330    #[must_use]
331    pub fn count_newlines(&self, buffer: BufferRef, offset: u32, len: u32) -> u32 {
332        bytecount::count(self.get_slice(buffer, offset, len).as_bytes(), b'\n') as _
333    }
334
335    #[inline(always)]
336    fn get_newlines(&self, buffer: BufferRef) -> &[u32] {
337        if buffer == MOD_BUFFER {
338            &self.modifications_newline_offsets
339        } else {
340            &self.original_buffers[buffer].newline_offsets
341        }
342    }
343
344    #[inline(always)]
345    fn get_checkpoints(&self, buffer: BufferRef) -> &[(u32, u32)] {
346        if buffer == MOD_BUFFER {
347            &self.modifications_char_checkpoints
348        } else {
349            &self.original_buffers[buffer].char_checkpoints
350        }
351    }
352
353    /// Converts an absolute char index to an absolute byte offset for a specific buffer
354    #[must_use]
355    #[inline]
356    pub fn char_to_byte_absolute(&self, buffer: BufferRef, target_char: u32) -> u32 {
357        if target_char == 0 { return 0; }
358
359        let checkpoints = self.get_checkpoints(buffer);
360        let cp_index = checkpoints.partition_point(|&(c, _)| c <= target_char);
361
362        let (current_char, current_byte) = if cp_index == 0 {
363            (0, 0)
364        } else {
365            unsafe { *checkpoints.get_unchecked(cp_index - 1) }
366        };
367
368        let remainder_chars = target_char - current_char;
369        if remainder_chars == 0 { return current_byte; }
370
371        let text = self.get(buffer);
372        let slice = unsafe { text.get_unchecked(current_byte as usize..) };
373
374        let additional_bytes = slice
375            .char_indices()
376            .nth(remainder_chars as usize)
377            .map(|(b, _)| b as u32)
378            .unwrap_or_else(|| slice.len() as u32);  // Fallback to end if out of bounds
379
380        current_byte + additional_bytes
381    }
382
383    /// Converts an absolute byte offset to an absolute char index for a specific buffer
384    #[must_use]
385    #[inline]
386    pub fn byte_to_char_absolute(&self, buffer: BufferRef, target_byte: u32) -> u32 {
387        if target_byte == 0 { return 0; }
388
389        let checkpoints = self.get_checkpoints(buffer);
390        let cp_index = checkpoints.partition_point(|&(_, b)| b <= target_byte);
391
392        let (current_char, current_byte) = if cp_index == 0 {
393            (0, 0)
394        } else {
395            unsafe { *checkpoints.get_unchecked(cp_index - 1) }
396        };
397
398        let remainder_bytes = target_byte - current_byte;
399        if remainder_bytes == 0 { return current_char; }
400
401        let text = self.get(buffer);
402        let slice = unsafe { text.get_unchecked(current_byte as usize..target_byte as usize) };
403        let additional_chars = bytecount::num_chars(slice.as_bytes()) as u32;
404
405        current_char + additional_chars
406    }
407}
408
409#[derive(Debug, Clone)]
410pub struct Pieces {
411    pub nodes: PrimaryMap<NodeRef, Node>,
412}
413
414impl Default for Pieces {
415    #[inline(always)]
416    fn default() -> Self {
417        Self::new()
418    }
419}
420
421impl Pieces {
422    #[inline(always)]
423    #[must_use]
424    pub fn new() -> Self {
425        let mut nodes = PrimaryMap::new();
426        nodes.push(Node {
427            left: NIL, right: NIL, offset: 0,
428            subtree_len: 0, subtree_chars: 0, subtree_newlines: 0,
429            meta: 0, buffer_start_line: 0,
430            piece_start_char: 0
431        });
432        Self { nodes }
433    }
434
435    #[inline(always)]
436    #[must_use]
437    pub fn len(&self) -> usize { self.nodes.len() }
438
439    #[inline(always)]
440    #[must_use]
441    pub fn is_empty(&self) -> bool { self.nodes.is_empty() }
442
443    #[inline(always)]
444    #[must_use]
445    pub fn get(&self, index: NodeRef) -> &Node { &self.nodes[index] }
446
447    #[inline(always)]
448    #[must_use]
449    pub fn get_piece(&self, index: NodeRef) -> Piece {
450        if index == NIL { return Piece::default(); }
451
452        let node = &self.nodes[index];
453        let l = &self.nodes[node.left];
454        let r = &self.nodes[node.right];
455
456        Piece {
457            buffer: node.buffer_index(),
458            byte_offset: node.offset,
459            byte_length: node.subtree_len - l.subtree_len - r.subtree_len,
460            char_count: node.subtree_chars - l.subtree_chars - r.subtree_chars,
461            newline_count: node.subtree_newlines - l.subtree_newlines - r.subtree_newlines,
462            buffer_start_line: node.buffer_start_line,
463            piece_start_char: node.piece_start_char,
464        }
465    }
466
467    #[inline(always)]
468    pub fn alloc(&mut self, color: Color, left: NodeRef, piece: Piece, right: NodeRef) -> NodeRef {
469        let l = &self.nodes[left];
470        let r = &self.nodes[right];
471
472        let mut node = Node {
473            left, right,
474            offset: piece.byte_offset,
475            subtree_len: l.subtree_len + piece.byte_length + r.subtree_len,
476            subtree_chars: l.subtree_chars + piece.char_count + r.subtree_chars,
477            subtree_newlines: l.subtree_newlines + piece.newline_count + r.subtree_newlines,
478            buffer_start_line: piece.buffer_start_line,
479            piece_start_char: piece.piece_start_char,
480            meta: 0,
481        };
482        node.set_color(color);
483        node.set_buffer(piece.buffer);
484
485        self.nodes.push(node)
486    }
487
488    /// Extends a piece without triggering Red-Black structural changes.
489    /// It creates a single new path to the root to maintain persistence.
490    pub fn extend_piece(
491        &mut self,
492        root: NodeRef,
493        piece_start_offset: u32,
494        add_len: u32,
495        add_chars: u32,
496        add_nl: u32,
497    ) -> NodeRef {
498        if root == NIL { return NIL }
499
500        let node = self.nodes[root];
501        let mut new_node = node;
502
503        let left_len = self.nodes[node.left].subtree_len;
504        let piece_len = node.subtree_len - left_len - self.nodes[node.right].subtree_len;
505
506        if piece_start_offset < left_len {
507            new_node.left  = self.extend_piece(node.left, piece_start_offset, add_len, add_chars, add_nl);
508        } else if piece_start_offset > left_len {
509            new_node.right = self.extend_piece(node.right, piece_start_offset - (left_len + piece_len), add_len, add_chars, add_nl);
510        } else {
511            // piece_start_offset == left_len
512            // We found the exact node! No further recursion needed.
513        }
514
515        new_node.subtree_len      += add_len;
516        new_node.subtree_chars    += add_chars;
517        new_node.subtree_newlines += add_nl;
518
519        self.nodes.push(new_node)
520    }
521
522    #[inline]
523    pub fn insert_node(&mut self, root: NodeRef, piece: Piece, at: u32) -> NodeRef {
524        let new_root = self.ins(root, piece, at, 0);
525        let r_node = self.nodes[new_root];
526        let p = self.get_piece(new_root);
527        self.alloc(Color::Black, r_node.left, p, r_node.right)
528    }
529
530    #[inline]
531    pub fn remove_node(&mut self, root: NodeRef, at: u32) -> NodeRef {
532        let new_root = self.rem(root, at, 0);
533        if new_root == NIL { return NIL }
534
535        let r_node = self.nodes[new_root];
536        self.alloc(Color::Black, r_node.left, self.get_piece(new_root), r_node.right)
537    }
538
539    #[inline]
540    fn rem(&mut self, root: NodeRef, at: u32, total: u32) -> NodeRef {
541        if root == NIL { return NIL }
542
543        let node = self.nodes[root];
544        let node_piece = self.get_piece(root);
545        let left_len = self.nodes[node.left].subtree_len;
546
547        match at.cmp(&(total + left_len)) {
548            Ordering::Less => self.remove_left(root, at, total),
549            Ordering::Equal => self.fuse(node.left, node.right),
550            Ordering::Greater => {
551                let next_total = total + left_len + node_piece.byte_length;
552                self.remove_right(root, at, next_total)
553            }
554        }
555    }
556
557    #[inline]
558    fn remove_left(&mut self, root: NodeRef, at: u32, total: u32) -> NodeRef {
559        let node = self.nodes[root];
560        let new_left = self.rem(node.left, at, total);
561        let new_node = self.alloc(Color::Red, new_left, self.get_piece(root), node.right);
562        if self.nodes[node.left].color() == Color::Black {
563            self.balance_left(new_node)
564        } else {
565            new_node
566        }
567    }
568
569    #[inline]
570    fn remove_right(&mut self, root: NodeRef, at: u32, total: u32) -> NodeRef {
571        let node = self.nodes[root];
572        let new_right = self.rem(node.right, at, total);
573        let new_node = self.alloc(Color::Red, node.left, self.get_piece(root), new_right);
574        if self.nodes[node.right].color() == Color::Black {
575            self.balance_right(new_node)
576        } else {
577            new_node
578        }
579    }
580
581    #[inline]
582    fn ins(&mut self, root: NodeRef, p: Piece, at: u32, total_offset: u32) -> NodeRef {
583        if root == NIL { return self.alloc(Color::Red, NIL, p, NIL); }
584
585        let node = self.nodes[root];
586        let node_piece = self.get_piece(root);
587        let left_len = self.nodes[node.left].subtree_len;
588
589        if at < total_offset + left_len + node_piece.byte_length {
590            let lft = self.ins(node.left, p, at, total_offset);
591            self.balance(node.color(), lft, node_piece, node.right)
592        } else {
593            let next_offset = total_offset + left_len + node_piece.byte_length;
594            let rgt = self.ins(node.right, p, at, next_offset);
595            self.balance(node.color(), node.left, node_piece, rgt)
596        }
597    }
598
599    #[inline]
600    fn balance(&mut self, c: Color, l_index: NodeRef, p: Piece, r_index: NodeRef) -> NodeRef {
601        let l = self.nodes[l_index];
602        let r = self.nodes[r_index];
603
604        if c == Color::Black {
605            if l.color() == Color::Red {
606                let ll = self.nodes[l.left];
607                let lr = self.nodes[l.right];
608                if ll.color() == Color::Red {
609                    let new_l = self.alloc(Color::Black, ll.left, self.get_piece(l.left), ll.right);
610                    let new_r = self.alloc(Color::Black, l.right, p, r_index);
611                    return self.alloc(Color::Red, new_l, self.get_piece(l_index), new_r);
612                } else if lr.color() == Color::Red {
613                    let new_l = self.alloc(Color::Black, l.left, self.get_piece(l_index), lr.left);
614                    let new_r = self.alloc(Color::Black, lr.right, p, r_index);
615                    return self.alloc(Color::Red, new_l, self.get_piece(l.right), new_r);
616                }
617            }
618
619            if r.color() == Color::Red {
620                let rl = self.nodes[r.left];
621                let rr = self.nodes[r.right];
622                if rl.color() == Color::Red {
623                    let new_l = self.alloc(Color::Black, l_index, p, rl.left);
624                    let new_r = self.alloc(Color::Black, rl.right, self.get_piece(r_index), r.right);
625                    return self.alloc(Color::Red, new_l, self.get_piece(r.left), new_r);
626                } else if rr.color() == Color::Red {
627                    let new_l = self.alloc(Color::Black, l_index, p, r.left);
628                    let new_r = self.alloc(Color::Black, rr.left, self.get_piece(r.right), rr.right);
629                    return self.alloc(Color::Red, new_l, self.get_piece(r_index), new_r);
630                }
631            }
632        }
633
634        self.alloc(c, l_index, p, r_index)
635    }
636
637    #[inline]
638    fn fuse(&mut self, left: NodeRef, right: NodeRef) -> NodeRef {
639        // match: (left, right)
640
641        // case: (None, r)
642        if left  == NIL { return right }
643        if right == NIL { return left }
644
645        let l_node = self.nodes[left];
646        let r_node = self.nodes[right];
647
648        // match: (left.color, right.color)
649
650        // case: (B, R)
651        if l_node.color() == Color::Black && r_node.color() == Color::Red {
652            let fused = self.fuse(left, r_node.left);
653            return self.alloc(Color::Red, fused, self.get_piece(right), r_node.right);
654        }
655
656        // case: (R, B)
657        if l_node.color() == Color::Red && r_node.color() == Color::Black {
658            let fused = self.fuse(l_node.right, right);
659            return self.alloc(Color::Red, l_node.left, self.get_piece(left), fused);
660        }
661
662        // case: (R, R)
663        if l_node.color() == Color::Red && r_node.color() == Color::Red {
664            let fused = self.fuse(l_node.right, r_node.left);
665            let f_node = self.nodes[fused];
666            if fused != NIL && f_node.color() == Color::Red {
667                let new_l = self.alloc(Color::Red, l_node.left, self.get_piece(left), f_node.left);
668                let new_r = self.alloc(Color::Red, f_node.right, self.get_piece(right), r_node.right);
669                return self.alloc(Color::Red, new_l, self.get_piece(fused), new_r);
670            }
671            let new_r = self.alloc(Color::Red, fused, self.get_piece(right), r_node.right);
672            return self.alloc(Color::Red, l_node.left, self.get_piece(left), new_r);
673        }
674
675        // case: (B, B)
676
677        let fused = self.fuse(l_node.right, r_node.left);
678        let f_node = self.nodes[fused];
679        if fused != NIL && f_node.color() == Color::Red {
680            let new_l = self.alloc(Color::Black, l_node.left, self.get_piece(left), f_node.left);
681            let new_r = self.alloc(Color::Black, f_node.right, self.get_piece(right), r_node.right);
682            return self.alloc(Color::Red, new_l, self.get_piece(fused), new_r);
683        }
684
685        let new_r = self.alloc(Color::Black, fused, self.get_piece(right), r_node.right);
686        let new_node = self.alloc(Color::Red, l_node.left, self.get_piece(left), new_r);
687        self.balance_left(new_node)
688    }
689
690    #[inline]
691    fn balance_left(&mut self, left: NodeRef) -> NodeRef {
692        let l_node = self.nodes[left];
693        let ll_node = self.nodes[l_node.left];
694        let lr_node = self.nodes[l_node.right];
695
696        // match: (color_l, color_r, color_r_l)
697
698        // case: (Some(R), ..)
699        if l_node.left != NIL && ll_node.color() == Color::Red {
700            let new_ll = self.alloc(Color::Black, ll_node.left, self.get_piece(l_node.left), ll_node.right);
701            return self.alloc(Color::Red, new_ll, self.get_piece(left), l_node.right);
702        }
703
704        // case: (_, Some(B), _)
705        if l_node.right != NIL && lr_node.color() == Color::Black {
706            let new_lr = self.alloc(Color::Red, lr_node.left, self.get_piece(l_node.right), lr_node.right);
707            let new_l = self.alloc(Color::Black, l_node.left, self.get_piece(left), new_lr);
708            let nl = self.nodes[new_l];
709            return self.balance(Color::Black, nl.left, self.get_piece(new_l), nl.right);
710        }
711
712        // case: (_, Some(R), Some(B))
713        if l_node.right != NIL && lr_node.color() == Color::Red {
714            let lrl_node = self.nodes[lr_node.left];
715            if lr_node.left != NIL && lrl_node.color() == Color::Black {
716                let lrr_node = self.nodes[lr_node.right];
717                let new_lrr = self.alloc(Color::Red, lrr_node.left, self.get_piece(lr_node.right), lrr_node.right);
718                let unbal_r = self.alloc(Color::Black, lrl_node.right, self.get_piece(l_node.right), new_lrr);
719                let ur_node = self.nodes[unbal_r];
720                let new_r = self.balance(ur_node.color(), ur_node.left, self.get_piece(unbal_r), ur_node.right);
721                let new_l = self.alloc(Color::Black, l_node.left, self.get_piece(left), lrl_node.left);
722                return self.alloc(Color::Red, new_l, self.get_piece(lr_node.left), new_r);
723            }
724        }
725
726        left
727    }
728
729    #[inline]
730    fn balance_right(&mut self, right: NodeRef) -> NodeRef {
731        let r_node = self.nodes[right];
732        let rl_node = self.nodes[r_node.left];
733        let rr_node = self.nodes[r_node.right];
734
735        // match: (color_l, color_l_r, color_r)
736
737        // case: (.., Some(R))
738        if r_node.right != NIL && rr_node.color() == Color::Red {
739            let new_rr = self.alloc(Color::Black, rr_node.left, self.get_piece(r_node.right), rr_node.right);
740            return self.alloc(Color::Red, r_node.left, self.get_piece(right), new_rr);
741        }
742
743        // case: (Some(B), ..)
744        if r_node.left != NIL && rl_node.color() == Color::Black {
745            let new_rl = self.alloc(Color::Red, rl_node.left, self.get_piece(r_node.left), rl_node.right);
746            let new_r = self.alloc(Color::Black, new_rl, self.get_piece(right), r_node.right);
747            let nr = self.nodes[new_r];
748            return self.balance(Color::Black, nr.left, self.get_piece(new_r), nr.right);
749        }
750
751        // case: (Some(R), Some(B), _)
752        if r_node.left != NIL && rl_node.color() == Color::Red {
753            let rlr_node = self.nodes[rl_node.right];
754            if rl_node.right != NIL && rlr_node.color() == Color::Black {
755                let rll_node = self.nodes[rl_node.left];
756                let new_rll = self.alloc(Color::Red, rll_node.left, self.get_piece(rl_node.left), rll_node.right);
757                let unbal_l = self.alloc(Color::Black, new_rll, self.get_piece(r_node.left), rlr_node.left);
758                let ul_node = self.nodes[unbal_l];
759                let new_l = self.balance(ul_node.color(), ul_node.left, self.get_piece(unbal_l), ul_node.right);
760                let new_r = self.alloc(Color::Black, rlr_node.right, self.get_piece(right), r_node.right);
761                return self.alloc(Color::Red, new_l, self.get_piece(rl_node.right), new_r);
762            }
763        }
764
765        right
766    }
767
768    #[inline]
769    #[must_use]
770    pub fn find_offset(
771        &self,
772        mut root: NodeRef,
773        mut target_offset: u32,
774        prefer_left: bool
775    ) -> Option<(NodeRef, u32)> {
776        while root != NIL {
777            let node = &self.nodes[root];
778            let left_len = self.nodes[node.left].subtree_len;
779            let piece_len = self.get_piece(root).byte_length;
780
781            if target_offset < left_len {
782                root = node.left;
783
784            } else if target_offset == left_len + piece_len && prefer_left {
785                // We are perfectly on the boundary and want to merge with the left piece
786                return Some((root, piece_len));
787
788            } else if target_offset < left_len + piece_len {
789                return Some((root, target_offset - left_len));
790
791            } else {
792                target_offset -= left_len + piece_len;
793                root = node.right;
794            }
795        }
796
797        None
798    }
799}
800
801impl PieceTree {
802    /// Slices a tree exactly at `offset`, returning (LeftTree, RightTree)
803    #[inline]
804    pub fn split(&mut self, root: NodeRef, offset: u32) -> (NodeRef, NodeRef) {
805        if root == NIL {
806            return (NIL, NIL);
807        }
808
809        let node = self.pieces.nodes[root];
810        let p = self.pieces.get_piece(root);
811        let left_len = self.pieces.nodes[node.left].subtree_len;
812
813        if offset < left_len {
814            let (ll, lr) = self.split(node.left, offset);
815
816            // Fast path: Use join_with_middle directly
817            let new_right = self.pieces.join_with_middle(lr, p, node.right);
818            (ll, new_right)
819
820        } else if offset > left_len + p.byte_length {
821            let (rl, rr) = self.split(node.right, offset - left_len - p.byte_length);
822
823            // Fast path: Use join_with_middle directly
824            let new_left = self.pieces.join_with_middle(node.left, p, rl);
825            (new_left, rr)
826
827        } else {
828            let rel_offset = offset - left_len;
829
830            if rel_offset == 0 {
831                (node.left, self.pieces.join_with_middle(NIL, p, node.right))
832
833            } else if rel_offset == p.byte_length {
834                (self.pieces.join_with_middle(node.left, p, NIL), node.right)
835
836            } else {
837                //
838                // We have to slice the Piece exactly in half!
839                //
840                let left_len = rel_offset;
841                let right_len = p.byte_length - rel_offset;
842
843                //
844                // Only scan the smaller half of the piece to avoid O(N) bottlenecks
845                //
846                let (left_chars, left_nl) = if left_len <= right_len {
847                    // Left side is shorter, scan normally
848                    self.buffers.count_chars_and_newlines(p.buffer, p.byte_offset, left_len)
849                } else {
850                    // Right side is shorter, scan it and subtract from the known totals
851                    let (r_chars, r_nl) = self.buffers.count_chars_and_newlines(
852                        p.buffer, p.byte_offset + left_len, right_len
853                    );
854
855                    (p.char_count - r_chars, p.newline_count - r_nl)
856                };
857
858                let left_stump = Piece {
859                    buffer: p.buffer,
860                    byte_offset: p.byte_offset,
861                    byte_length: left_len,
862                    newline_count: left_nl,
863                    char_count: left_chars,
864                    buffer_start_line: p.buffer_start_line,
865                    piece_start_char: p.piece_start_char,
866                };
867
868                let right_stump = Piece {
869                    buffer: p.buffer,
870                    byte_offset: p.byte_offset + left_len,
871                    byte_length: right_len,
872                    newline_count: p.newline_count - left_nl,
873                    char_count: p.char_count - left_chars,
874                    buffer_start_line: p.buffer_start_line + left_nl,
875                    piece_start_char: p.piece_start_char + left_chars,
876                };
877
878                let new_left = self.pieces.join_with_middle(node.left, left_stump, NIL);
879                let new_right = self.pieces.join_with_middle(NIL, right_stump, node.right);
880                (new_left, new_right)
881            }
882        }
883    }
884
885    /// Glues two arbitrary trees together by extracting the max element of the left tree
886    #[inline]
887    pub fn concat(&mut self, left: NodeRef, right: NodeRef) -> NodeRef {
888        //
889        // Group the evaluation so we can force a Black root on the way out!
890        //
891        let new_root = if left == NIL {
892            right
893        } else if right == NIL {
894            left
895        } else {
896            let mut curr = left;
897            while self.pieces.nodes[curr].right != NIL {
898                curr = self.pieces.nodes[curr].right;
899            }
900            let max_piece = self.pieces.get_piece(curr);
901            let max_offset = self.pieces.nodes[left].subtree_len - max_piece.byte_length;
902
903            let left_without_max = self.pieces.remove_node(left, max_offset);
904
905            self.pieces.join_with_middle(left_without_max, max_piece, right)
906        };
907
908        //
909        // Even if we early-returned right, we MUST ensure the final returned root is Black.
910        //
911        if new_root != NIL && self.pieces.nodes[new_root].color() == Color::Red {
912            let p = self.pieces.get_piece(new_root);
913            let r = self.pieces.nodes[new_root];
914            return self.pieces.alloc(Color::Black, r.left, p, r.right);
915        }
916
917        new_root
918    }
919}
920
921impl Pieces {
922    /// Recursively counts the black height of a given subtree
923    #[inline(always)]
924    pub fn black_height(&self, mut node: NodeRef) -> u32 {
925        let mut h = 0;
926        while node != NIL {
927            if self.nodes[node].color() == Color::Black {
928                h += 1;
929            }
930            node = self.nodes[node].left;
931        }
932        h
933    }
934
935    /// Safely joins two arbitrary Red-Black trees using a middle element
936    #[inline(always)]
937    pub fn join_with_middle(&mut self, left: NodeRef, piece: Piece, right: NodeRef) -> NodeRef {
938        let hl = self.black_height(left);
939        let hr = self.black_height(right);
940
941        let new_root = if hl > hr {
942            self.join_right(left, piece, right, hl, hr)
943        } else if hr > hl {
944            self.join_left(left, piece, right, hl, hr)
945        } else {
946            self.alloc(Color::Red, left, piece, right)
947        };
948
949        // Enforce the Red-Black root constraint!
950        if new_root != NIL && self.nodes[new_root].color() == Color::Red {
951            let p = self.get_piece(new_root);
952            let r = self.nodes[new_root];
953            return self.alloc(Color::Black, r.left, p, r.right);
954        }
955
956        new_root
957    }
958
959    #[inline(always)]
960    fn join_right(&mut self, left: NodeRef, piece: Piece, right: NodeRef, hl: u32, hr: u32) -> NodeRef {
961        if hl == hr {
962            return self.alloc(Color::Red, left, piece, right);
963        }
964
965        let l_node = self.nodes[left];
966        let next_hl = if l_node.color() == Color::Black { hl - 1 } else { hl };
967
968        let new_right = self.join_right(l_node.right, piece, right, next_hl, hr);
969        let p = self.get_piece(left);
970
971        self.balance(l_node.color(), l_node.left, p, new_right)
972    }
973
974    #[inline(always)]
975    fn join_left(&mut self, left: NodeRef, piece: Piece, right: NodeRef, hl: u32, hr: u32) -> NodeRef {
976        if hl == hr {
977            return self.alloc(Color::Red, left, piece, right);
978        }
979
980        let r_node = self.nodes[right];
981        let next_hr = if r_node.color() == Color::Black { hr - 1 } else { hr };
982
983        let new_left = self.join_left(left, piece, r_node.left, hl, next_hr);
984        let p = self.get_piece(right);
985
986        self.balance(r_node.color(), new_left, p, r_node.right)
987    }
988}
989
990#[derive(Default, Debug, Clone)]
991pub struct PieceTree {
992    //
993    // `last_insert_end`      is the mod-buffer offset one past the last inserted byte.
994    // `last_insert_tree_end` is the document   offset one past that insertion.
995    //
996    // Both are u32::MAX when unset (after removes, undos, or on init).
997    //
998    last_mod_end:  u32,  // fredbuf's last_insert     (BufferCursor)
999    last_tree_end: u32,  // fredbuf's end_last_insert (CharOffset)
1000
1001    pub root: NodeRef,
1002
1003    /// Tracks nested undo groups. 0 if we are outside any group.
1004    pub transaction_depth: usize,
1005
1006    pub undo_stack: Vec<HistoryEntry>,
1007    pub redo_stack: Vec<HistoryEntry>,
1008
1009    pub pieces:  Pieces,
1010    pub buffers: Buffers,
1011
1012    pub scratch_index_map: Vec<u32>,
1013}
1014
1015impl core::fmt::Display for PieceTree {
1016    #[inline(always)]
1017    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1018        let walker = TreeWalker::new(self);
1019        for char in walker {
1020            write!(f, "{char}")?;
1021        }
1022
1023        Ok(())
1024    }
1025}
1026
1027impl<T> From<T> for PieceTree where T: AsRef<str> {
1028    #[inline(always)]
1029    fn from(value: T) -> Self {
1030        Self::from_str(value.as_ref())
1031    }
1032}
1033
1034impl str::FromStr for PieceTree {
1035    type Err = core::convert::Infallible;
1036
1037    #[inline(always)]
1038    fn from_str(s: &str) -> Result<Self, Self::Err> {
1039        Ok(PieceTree::from(s))
1040    }
1041}
1042
1043impl PieceTree {
1044    #[inline(always)]
1045    #[must_use]
1046    pub fn new() -> Self {
1047        Self::default()
1048    }
1049
1050    #[inline(always)]
1051    #[must_use]
1052    pub fn from_str(text: &str) -> Self {
1053        #[inline(always)]
1054        fn is_utf8_char_boundary(b: u8) -> bool { (b as i8) >= -0x40 }
1055
1056        let mut tree = PieceTree::new();
1057
1058        let mut offset = 0;
1059
1060        let text_bytes = text.as_bytes();
1061        let total_len = text.len() as u32;
1062
1063        while offset < total_len {
1064            // Find the end of the chunk
1065            let mut chunk_len = (total_len - offset).min(MAX_PIECE_SIZE);
1066
1067            // SAFETY: Ensure we don't slice a UTF-8 character in half!
1068            while offset + chunk_len < total_len && !is_utf8_char_boundary(text_bytes[(offset + chunk_len) as usize]) {
1069                chunk_len += 1;
1070            }
1071
1072            let chunk_text = &text[offset as usize .. (offset + chunk_len) as usize];
1073
1074            // Insert the chunk at the very end of the tree
1075            tree.insert(tree.total_length(), chunk_text);
1076
1077            offset += chunk_len;
1078        }
1079
1080        tree
1081    }
1082}
1083
1084impl PieceTree {
1085    /// Starts a new undo group, saves the current state if this is the outermost group.
1086    #[inline]
1087    pub fn begin_undo_group(&mut self, cursor_offset: u32) {
1088        if self.transaction_depth == 0 {
1089            self.commit_head(cursor_offset);
1090        }
1091
1092        self.transaction_depth += 1;
1093    }
1094
1095    /// Ends the current undo group
1096    #[inline]
1097    pub fn end_undo_group(&mut self) {
1098        if self.transaction_depth > 0 {
1099            self.transaction_depth -= 1;
1100        }
1101    }
1102
1103    #[inline(always)]
1104    pub fn commit_head(&mut self, cursor_offset: u32) {
1105        if let Some(last) = self.undo_stack.last() {
1106            if last.root == self.root {
1107                return;  // Root hasn't changed, skip duplication
1108            }
1109        }
1110
1111        self.undo_stack.push(HistoryEntry { root: self.root, cursor_offset });
1112        self.redo_stack.clear();
1113    }
1114
1115    #[inline(always)]
1116    pub fn try_undo(&mut self, current_cursor: u32) -> Option<u32> {
1117        if self.transaction_depth > 0 {
1118            return None;
1119        }
1120
1121        if let Some(entry) = self.undo_stack.pop() {
1122            self.last_tree_end = u32::MAX;
1123            self.last_mod_end  = u32::MAX;
1124
1125            self.redo_stack.push(HistoryEntry {
1126                root: self.root, cursor_offset: current_cursor
1127            });
1128            self.root = entry.root;
1129            return Some(entry.cursor_offset);
1130        }
1131
1132        None
1133    }
1134
1135    #[inline(always)]
1136    pub fn try_redo(&mut self, current_cursor: u32) -> Option<u32> {
1137        if let Some(entry) = self.redo_stack.pop() {
1138            self.last_tree_end = u32::MAX;
1139            self.last_mod_end  = u32::MAX;
1140
1141            self.undo_stack.push(HistoryEntry {
1142                root: self.root, cursor_offset: current_cursor
1143            });
1144            self.root = entry.root;
1145            return Some(entry.cursor_offset);
1146        }
1147
1148        None
1149    }
1150
1151    #[inline(always)]
1152    #[must_use]
1153    pub fn total_length(&self) -> u32 {
1154        self.pieces.get(self.root).subtree_len
1155    }
1156
1157    #[inline(always)]
1158    pub fn apply_edits(&mut self, primary_cursor_offset: u32, edits: &mut [Edit]) {
1159        if edits.is_empty() { return }
1160
1161        self.begin_undo_group(primary_cursor_offset);
1162
1163        edits.sort_by_key(|b| core::cmp::Reverse(b.offset()));
1164        for edit in edits {
1165            match edit {
1166                Edit::Insert { offset, text }   => self.insert_no_commit(*offset, text),
1167                Edit::Remove { offset, length } => self.remove_no_commit(*offset, *length),
1168            }
1169        }
1170
1171        self.end_undo_group();
1172    }
1173
1174    /// Inserts a single character at the specified logical byte offset.
1175    #[inline(always)]
1176    pub fn insert_char(&mut self, offset: u32, ch: char) {
1177        self.commit_head(offset);
1178        self.insert_char_no_commit(offset, ch);
1179    }
1180
1181    /// Inserts a single character at the specified logical byte offset.
1182    #[inline(always)]
1183    pub fn insert_char_no_commit(&mut self, offset: u32, ch: char) {
1184        let mut buf = [0; 4];
1185        self.insert_no_commit(offset, ch.encode_utf8(&mut buf));
1186    }
1187
1188    #[inline(always)]
1189    pub fn insert(&mut self, offset: u32, text: &str) {
1190        if text.is_empty() { return }
1191
1192        let auto_group = self.transaction_depth == 0;
1193        if auto_group {
1194            self.begin_undo_group(offset);
1195        }
1196
1197        self.insert_no_commit(offset, text);
1198
1199        if auto_group {
1200            self.end_undo_group();
1201        }
1202    }
1203
1204    #[inline(always)]
1205    pub fn remove_at(&mut self, offset: u32, length: u32) {
1206        if length == 0 || self.root == NIL { return }
1207
1208        let auto_group = self.transaction_depth == 0;
1209        if auto_group {
1210            self.begin_undo_group(offset);
1211        }
1212
1213        self.remove_no_commit(offset, length);
1214
1215        if auto_group {
1216            self.end_undo_group();
1217        }
1218    }
1219
1220    pub fn insert_no_commit(&mut self, offset: u32, text: &str) {
1221        let mod_offset = self.buffers.modifications_buffer.len() as u32;
1222        let start_line_in_buffer = self.buffers.modifications_newline_offsets.len() as u32;
1223
1224        let mut total_char_count = if let Some(&(c, b)) = self.buffers.modifications_char_checkpoints.last() {
1225            let tail_bytes = &self.buffers.modifications_buffer[b as usize..mod_offset as usize];
1226            c + bytecount::num_chars(tail_bytes.as_bytes()) as u32
1227        } else {
1228            bytecount::num_chars(self.buffers.modifications_buffer[..mod_offset as usize].as_bytes()) as u32
1229        };
1230
1231        let mut newline_count = 0;
1232        let mut char_count = 0;
1233        {
1234            let rem = total_char_count % CHECKPOINT_INTERVAL;
1235            let mut next_checkpoint = total_char_count + ((CHECKPOINT_INTERVAL - rem) % CHECKPOINT_INTERVAL);
1236
1237            if next_checkpoint == 0 {
1238                next_checkpoint = CHECKPOINT_INTERVAL;
1239            }
1240
1241            //
1242            // @Cutnpaste from count_chars_and_newlines_with_offsets_and_checkpoints
1243            //
1244            for (i, b) in text.bytes().enumerate() {
1245                if (b as i8) >= -0x40 {
1246                    if total_char_count == next_checkpoint {
1247                        self.buffers.modifications_char_checkpoints.push((total_char_count, mod_offset + i as u32));
1248                        next_checkpoint += CHECKPOINT_INTERVAL;
1249                    }
1250
1251                    total_char_count += 1;
1252                    char_count += 1;
1253                }
1254
1255                if b == b'\n' {
1256                    self.buffers.modifications_newline_offsets.push(mod_offset + i as u32);
1257                    newline_count += 1;
1258                }
1259            }
1260        }
1261
1262        self.buffers.modifications_buffer.push_str(text);
1263        let text_len = text.len() as u32;
1264
1265        let new_mod_end  = mod_offset + text_len;
1266        let new_tree_end = offset + text_len;
1267
1268        //
1269        // Fast path for sequential typing
1270        //
1271        if offset > 0
1272        && self.last_tree_end == offset
1273        && self.last_mod_end == mod_offset
1274        && let Some((prev_index, prev_rel)) = self.find_position(offset - 1, false)
1275        {
1276            let prev = self.pieces.get_piece(prev_index);
1277
1278            //
1279            // Predecessor must be a mod-buf piece ending at mod_offset.
1280            //
1281            if prev.buffer == MOD_BUFFER && prev.byte_offset + prev.byte_length == mod_offset {
1282                let prev_start = (offset - 1) - prev_rel;
1283
1284                self.root = self.pieces.extend_piece(
1285                    self.root,
1286                    prev_start,
1287                    text_len,
1288                    char_count,
1289                    newline_count
1290                );
1291
1292                self.last_mod_end  = new_mod_end;
1293                self.last_tree_end = new_tree_end;
1294                return;
1295            }
1296        }
1297
1298        let new_piece = Piece {
1299            buffer: MOD_BUFFER,
1300            byte_offset: mod_offset,
1301            byte_length: text_len,
1302            newline_count,
1303            char_count,
1304            buffer_start_line: start_line_in_buffer,
1305            piece_start_char:  total_char_count - char_count,
1306        };
1307
1308        if self.root == NIL {
1309            self.root = self.pieces.insert_node(self.root, new_piece, offset);
1310        } else {
1311            //
1312            // Snip the tree exactly at the insertion offset
1313            //
1314            let (left, right) = self.split(self.root, offset);
1315
1316            //
1317            // Sandwich the new piece directly between the left and right trees!
1318            //
1319            self.root = self.pieces.join_with_middle(left, new_piece, right);
1320
1321            //
1322            // Clean up the resulting seams
1323            //
1324
1325            // Attempt to merge the left side with new_piece
1326            self.try_merge_at(offset);
1327
1328            // Attempt to merge new_piece with the right side
1329            self.try_merge_at(offset + text_len);
1330        }
1331
1332        self.last_mod_end  = new_mod_end;
1333        self.last_tree_end = new_tree_end;
1334    }
1335
1336    pub fn remove_no_commit(&mut self, offset: u32, mut length: u32) {
1337        let total = self.total_length();
1338        if offset >= total { return; }
1339        if offset + length > total { length = total - offset; }
1340        if length == 0 { return }
1341
1342        //
1343        // Snip off the portion of the tree that comes BEFORE the deletion
1344        //
1345        let (left, remainder) = self.split(self.root, offset);
1346
1347        //
1348        // Snip the deleted portion out of the remainder
1349        //
1350        let (_deleted, right) = self.split(remainder, length);
1351
1352        //
1353        // Glue the surviving outer halves back together
1354        //
1355        self.root = self.concat(left, right);
1356
1357        //
1358        // Clean up the resulting seam
1359        //
1360        self.try_merge_at(offset);
1361    }
1362
1363    #[inline]
1364    fn try_merge_at(&mut self, pos: u32) {
1365        if pos == 0 { return }
1366
1367        let Some((right_index, right_rel)) = self.find_position(pos, false) else { return };
1368
1369        if right_rel != 0 { return }
1370
1371        let right = self.pieces.get_piece(right_index);
1372        self.try_merge_right_with_left(pos, right);
1373    }
1374
1375    // Returns Some((merged_start, merged_piece)) if merge happened, None otherwise.
1376    #[inline]
1377    fn try_merge_right_with_left(&mut self, right_start: u32, right: Piece) -> Option<(u32, Piece)> {
1378        if right_start == 0 { return None; }
1379        if right.buffer != MOD_BUFFER { return None; }
1380
1381        let Some((left_index, left_rel)) = self.find_position(right_start - 1, false) else { return None };
1382        let left = self.pieces.get_piece(left_index);
1383
1384        if left.buffer != MOD_BUFFER { return None; }
1385        if left.byte_offset + left.byte_length != right.byte_offset { return None; }
1386
1387        let left_start = (right_start - 1) - left_rel;
1388
1389        self.root = self.pieces.remove_node(self.root, left_start);
1390        self.root = self.pieces.remove_node(self.root, left_start);
1391
1392        let merged = Piece {
1393            buffer:  MOD_BUFFER,
1394            byte_offset:        left.byte_offset,
1395            byte_length:        left.byte_length        + right.byte_length,
1396            newline_count: left.newline_count + right.newline_count,
1397            char_count:    left.char_count    + right.char_count,
1398            buffer_start_line: left.buffer_start_line,
1399            piece_start_char: left.piece_start_char,  // right side extends, left start unchanged
1400        };
1401        self.root = self.pieces.insert_node(self.root, merged, left_start);
1402        Some((left_start, merged))
1403    }
1404
1405    #[inline(always)]
1406    #[must_use]
1407    pub fn get_piece(&self, index: NodeRef) -> Piece {
1408        self.pieces.get_piece(index)
1409    }
1410
1411    #[inline]
1412    #[must_use]
1413    pub fn find_position(&self, offset: u32, prefer_left: bool) -> Option<(NodeRef, u32)> {
1414        let total = self.total_length();
1415        if offset > total { return None; }
1416
1417        if offset == total && total > 0 {
1418            let mut current = self.root;
1419            let mut last_valid = NIL;
1420            while current != NIL {
1421                last_valid = current;
1422                current = self.pieces.get(current).right;
1423            }
1424            let len = self.pieces.get_piece(last_valid).byte_length;
1425            return Some((last_valid, len));
1426        }
1427
1428        self.pieces.find_offset(self.root, offset, prefer_left)
1429    }
1430
1431    #[cfg(feature = "write")]
1432    #[inline]
1433    pub fn write_to<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
1434        if self.root == NIL {
1435            return Ok(());
1436        }
1437
1438        for (_, piece) in self.pieces() {
1439            let slice = self.buffers.get_slice(piece.buffer, piece.byte_offset, piece.byte_length);
1440            writer.write_all(slice.as_bytes())?;
1441        }
1442
1443        writer.flush()
1444    }
1445}
1446
1447impl PieceTree {
1448    #[inline]
1449    pub fn compact(&mut self) {
1450        #[inline(always)]
1451        fn copy_node(
1452            old_index: NodeRef, old_arena: &Pieces,
1453            new_arena: &mut Pieces, index_map: &mut [u32]
1454        ) -> NodeRef {
1455            if old_index == NIL { return NIL }
1456
1457            if index_map[old_index.index()] != 0 {
1458                return NodeRef::new(index_map[old_index.index()] as usize);
1459            }
1460
1461            let node = old_arena.get(old_index);
1462            let left_new = copy_node(node.left, old_arena, new_arena, index_map);
1463            let right_new = copy_node(node.right, old_arena, new_arena, index_map);
1464
1465            let new_index = NodeRef::new(new_arena.nodes.len());
1466            new_arena.nodes.push(Node {
1467                left: left_new,
1468                right: right_new,
1469                offset: node.offset,
1470                subtree_len: node.subtree_len,
1471                subtree_chars: node.subtree_chars,
1472                subtree_newlines: node.subtree_newlines,
1473                piece_start_char: node.piece_start_char,
1474                meta: node.meta,
1475                buffer_start_line: node.buffer_start_line
1476            });
1477
1478            index_map[old_index.index()] = new_index.index() as u32;
1479            new_index
1480        }
1481
1482        let mut new_pieces = Pieces::new();
1483
1484        self.scratch_index_map.clear();
1485        self.scratch_index_map.resize(self.pieces.nodes.len(), 0);
1486
1487        self.root = copy_node(self.root, &self.pieces, &mut new_pieces, &mut self.scratch_index_map);
1488        for entry in &mut self.undo_stack {
1489            entry.root = copy_node(entry.root, &self.pieces, &mut new_pieces, &mut self.scratch_index_map);
1490        }
1491        for entry in &mut self.redo_stack {
1492            entry.root = copy_node(entry.root, &self.pieces, &mut new_pieces, &mut self.scratch_index_map);
1493        }
1494
1495        self.pieces = new_pieces;
1496    }
1497
1498    #[inline]
1499    pub fn squash(&mut self) {  // @Memory
1500        if self.root == NIL { return }
1501
1502        let squashed_text = self.to_string();  // @Memory
1503        let length = squashed_text.len() as u32;
1504
1505        let mut offsets = Vec::new();
1506        let mut checkpoints = Vec::new();
1507
1508        let (char_count, newline_count) =
1509            count_chars_and_newlines_with_offsets_and_checkpoints(
1510                squashed_text.as_bytes(),
1511                &mut offsets,
1512                &mut checkpoints
1513            );
1514
1515        self.buffers = Buffers::new();
1516        let buffer = self.buffers.original_buffers.push(OriginalBuffer {
1517            newline_offsets: offsets.into(),
1518            char_checkpoints: checkpoints.into(),
1519            text: squashed_text.into()
1520        });
1521        self.pieces = Pieces::new();
1522
1523        let piece = Piece {
1524            byte_length: length, newline_count, char_count,
1525            buffer, byte_offset: 0,
1526            buffer_start_line: 0, piece_start_char: 0
1527        };
1528        self.root = self.pieces.insert_node(NIL, piece, 0);
1529
1530        self.undo_stack.clear();
1531        self.redo_stack.clear();
1532    }
1533}
1534
1535impl PieceTree {
1536    /// Total bytes allocated for the node arena (includes NIL sentinel and
1537    /// all historical nodes retained for undo/redo).
1538    #[inline(always)]
1539    #[must_use]
1540    pub fn node_arena_bytes(&self) -> u32 {
1541        (self.pieces.nodes.len() * size_of::<Node>()) as _
1542    }
1543
1544    /// Bytes consumed by the modifications buffer (append-only, never shrinks).
1545    #[inline(always)]
1546    #[must_use]
1547    pub fn mod_buffer_bytes(&self) -> u32 {
1548        self.buffers.modifications_buffer.len() as _
1549    }
1550
1551    /// Bytes consumed by all original (read) buffers.
1552    #[inline(always)]
1553    #[must_use]
1554    pub fn original_buffers_bytes(&self) -> u32 {
1555        self.buffers.original_buffers.values().map(|s| s.len()).sum::<usize>() as _
1556    }
1557
1558    /// Bytes consumed by undo + redo history entries.
1559    #[inline(always)]
1560    #[must_use]
1561    pub fn history_bytes(&self) -> u32 {
1562        ((self.undo_stack.len() + self.redo_stack.len()) * size_of::<HistoryEntry>()) as _
1563    }
1564
1565    /// Number of live nodes in the arena (includes NIL and all historical nodes).
1566    #[inline(always)]
1567    #[must_use]
1568    pub fn node_count(&self) -> u32 {
1569        self.pieces.nodes.len() as _
1570    }
1571
1572    /// Aggregate memory usage breakdown.
1573    #[inline(always)]
1574    #[must_use]
1575    pub fn memory_usage(&self) -> MemoryUsage {
1576        MemoryUsage {
1577            node_arena:       self.node_arena_bytes(),
1578            mod_buffer:       self.mod_buffer_bytes(),
1579            original_buffers: self.original_buffers_bytes(),
1580            history:          self.history_bytes(),
1581        }
1582    }
1583}
1584
1585#[derive(Debug, Clone, Copy)]
1586pub struct MemoryUsage {
1587    pub node_arena:       u32,
1588    pub mod_buffer:       u32,
1589    pub original_buffers: u32,
1590    pub history:          u32,
1591}
1592
1593impl MemoryUsage {
1594    #[inline(always)]
1595    #[must_use]
1596    pub const fn total(&self) -> u32 {
1597        self.node_arena + self.mod_buffer + self.original_buffers + self.history
1598    }
1599
1600    /// Overhead = everything except the actual document content in buffers.
1601    #[inline(always)]
1602    #[must_use]
1603    pub const fn overhead(&self) -> u32 {
1604        self.node_arena + self.history
1605    }
1606}
1607
1608#[derive(Debug)]
1609pub struct LinesIter<'a> {
1610    tree: &'a PieceTree,
1611    current_line: u32,
1612    total_lines: u32,
1613}
1614
1615impl Iterator for LinesIter<'_> {
1616    type Item = String;
1617
1618    fn next(&mut self) -> Option<Self::Item> {
1619        if self.current_line >= self.total_lines { return None }
1620
1621        let line = self.tree.get_line_content_allocating(self.current_line)?;
1622        self.current_line += 1;
1623        Some(line)
1624    }
1625}
1626
1627/// A lazy, non-allocating slice view over a byte range of the tree
1628#[derive(Debug)]
1629pub struct ChunkIter<'a> {
1630    tree:       &'a PieceTree,
1631    pieces:     PieceTreeIter<'a>,
1632    // byte range we care about
1633    start:      u32,
1634    end:        u32,
1635    // bytes consumed so far across all pieces
1636    piece_start: u32,
1637}
1638
1639impl<'a> ChunkIter<'a> {
1640    #[inline]
1641    #[must_use]
1642    pub fn new(tree: &'a PieceTree, start: u32, end: u32) -> Self {
1643        Self {
1644            tree,
1645            pieces: PieceTreeIter::new(&tree.pieces, tree.root),
1646            start,
1647            end,
1648            piece_start: 0,
1649        }
1650    }
1651}
1652
1653impl<'a> Iterator for ChunkIter<'a> {
1654    type Item = &'a str;
1655
1656    #[inline]
1657    fn next(&mut self) -> Option<&'a str> {
1658        loop {
1659            let (_, p) = self.pieces.next()?;
1660            let piece_end = self.piece_start + p.byte_length;
1661
1662            // Skip pieces entirely before our window
1663            if piece_end <= self.start {
1664                self.piece_start = piece_end;
1665                continue;
1666            }
1667            // Stop once we're past our window
1668            if self.piece_start >= self.end {
1669                return None;
1670            }
1671
1672            let slice_start = self.start.saturating_sub(self.piece_start);
1673            let slice_end   = (self.end - self.piece_start).min(p.byte_length);
1674
1675            self.piece_start = piece_end;
1676
1677            let text = self.tree.buffers.get_slice(p.buffer, p.byte_offset + slice_start, slice_end - slice_start);
1678            if text.is_empty() { continue; }
1679            return Some(text);
1680        }
1681    }
1682}
1683
1684/// Non-allocating char iterator over a byte-bounded window of the tree.
1685#[derive(Debug)]
1686pub struct SliceChars<'a> {
1687    walker:   TreeWalker<'a>,
1688    byte_end: u32,
1689}
1690
1691impl Iterator for SliceChars<'_> {
1692    type Item = char;
1693    #[inline]
1694    fn next(&mut self) -> Option<char> {
1695        if self.walker.offset >= self.byte_end { return None; }
1696        self.walker.next()
1697    }
1698}
1699
1700/// A zero-copy borrowed view over a byte range of the tree.
1701/// All offsets are relative to the slice start.
1702pub struct TreeSlice<'a> {
1703    tree:  &'a PieceTree,
1704    start: u32,   // byte offset into tree (inclusive)
1705    end:   u32,   // byte offset into tree (exclusive)
1706}
1707
1708impl core::fmt::Display for TreeSlice<'_> {
1709    #[inline(always)]
1710    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1711        for char in self.chars() {
1712            write!(f, "{char}")?;
1713        }
1714        Ok(())
1715    }
1716}
1717
1718impl<'a> TreeSlice<'a> {
1719    #[inline(always)]
1720    #[must_use]
1721    pub fn new(tree: &'a PieceTree, start: u32, end: u32) -> Self {
1722        let end = end.min(tree.len_bytes());
1723        debug_assert!(start <= end);
1724        Self { tree, start, end }
1725    }
1726
1727    #[inline(always)]
1728    #[must_use]
1729    pub fn len_bytes(&self) -> u32  { self.end - self.start }
1730
1731    #[inline(always)]
1732    #[must_use]
1733    pub fn is_empty(&self)  -> bool { self.start == self.end }
1734
1735    #[inline(always)]
1736    #[must_use]
1737    pub fn len_chars(&self) -> u32 {
1738        let a = self.tree.byte_to_char(self.start).unwrap_or(0);
1739        let b = self.tree.byte_to_char(self.end).unwrap_or_else(|| self.tree.len_chars());
1740        b - a
1741    }
1742
1743    #[inline(always)]
1744    #[must_use]
1745    pub fn len_lines(&self) -> u32 {
1746        self.tree.chunks_at_byte(self.start, self.end)
1747            .map(|s| bytecount::count(s.as_bytes(), b'\n') as u32)
1748            .sum::<u32>() + 1
1749    }
1750
1751    /// Non-allocating chunk iterator over the slice's byte range.
1752    #[inline(always)]
1753    #[must_use]
1754    pub fn chunks(&self) -> ChunkIter<'a> {
1755        self.tree.chunks_at_byte(self.start, self.end)
1756    }
1757
1758    /// Non-allocating char iterator over the slice.
1759    #[inline(always)]
1760    #[must_use]
1761    pub fn chars(&self) -> SliceChars<'a> {
1762        self.tree.slice_bytes(self.start, self.end)
1763    }
1764
1765    /// Byte at a slice-relative byte offset.
1766    #[inline(always)]
1767    #[must_use]
1768    pub fn byte(&self, offset: u32) -> Option<u8> {
1769        if offset >= self.len_bytes() { return None; }
1770        self.tree.byte(self.start + offset)
1771    }
1772
1773    /// Char at a slice-relative char index.
1774    #[inline(always)]
1775    #[must_use]
1776    pub fn char(&self, char_index: u32) -> Option<char> {
1777        let abs_char = self.tree.byte_to_char(self.start)? + char_index;
1778        self.tree.char(abs_char)
1779    }
1780
1781    /// Non-allocating chunk iterator over a slice-relative line.
1782    #[inline(always)]
1783    #[must_use]
1784    pub fn line(&self, line: u32) -> Option<ChunkIter<'a>> {
1785        let (abs_start, abs_end) = self.abs_line_range(line)?;
1786        Some(self.tree.chunks_at_byte(abs_start, abs_end))
1787    }
1788
1789    /// Returns a sub-slice over a byte range (relative to this slice).
1790    #[inline(always)]
1791    #[must_use]
1792    pub fn slice<R: RangeBounds<u32>>(&self, range: R) -> TreeSlice<'a> {
1793        let s = match range.start_bound() {
1794            Bound::Included(&n) => n,
1795            Bound::Excluded(&n) => n + 1,
1796            Bound::Unbounded    => 0,
1797        };
1798        let e = match range.end_bound() {
1799            Bound::Included(&n) => n + 1,
1800            Bound::Excluded(&n) => n,
1801            Bound::Unbounded    => self.len_bytes(),
1802        };
1803        TreeSlice::new(self.tree, self.start + s, self.start + e)
1804    }
1805
1806    #[inline(always)]
1807    #[must_use]
1808    pub fn byte_to_char(&self, byte_offset: u32) -> Option<u32> {
1809        let base = self.tree.byte_to_char(self.start)?;
1810        let abs  = self.tree.byte_to_char(self.start + byte_offset)?;
1811        Some(abs - base)
1812    }
1813
1814    #[inline(always)]
1815    #[must_use]
1816    pub fn char_to_byte(&self, char_index: u32) -> Option<u32> {
1817        let base_char = self.tree.byte_to_char(self.start)?;
1818        let abs_byte  = self.tree.char_to_byte(base_char + char_index)?;
1819        Some(abs_byte - self.start)
1820    }
1821
1822    #[inline(always)]
1823    #[must_use]
1824    pub fn byte_to_line(&self, byte_offset: u32) -> Option<u32> {
1825        let (abs_line, _) = self.tree.byte_to_line_col(self.start + byte_offset)?;
1826        let base_line     = self.tree.byte_to_line_col(self.start).map(|(l, _)| l)?;
1827        Some(abs_line - base_line)
1828    }
1829
1830    #[inline(always)]
1831    #[must_use]
1832    pub fn line_to_byte(&self, line: u32) -> Option<u32> {
1833        let (abs_start, _) = self.abs_line_range(line)?;
1834        Some(abs_start - self.start)
1835    }
1836
1837    #[inline]
1838    #[must_use]
1839    pub fn abs_line_range(&self, rel_line: u32) -> Option<(u32, u32)> {
1840        let base_line = self.tree.byte_to_line_col(self.start).map(|(l, _)| l)?;
1841        let abs_line  = base_line + rel_line;
1842
1843        let line_start = self.tree.line_to_byte(abs_line)?;
1844        let line_end   = self.tree.line_to_byte(abs_line + 1)
1845            .unwrap_or_else(|| self.tree.len_bytes());
1846
1847        //
1848        // Clamp to slice bounds.
1849        //
1850        let s = line_start.max(self.start);
1851        let e = line_end.min(self.end);
1852        if s > e { return None; }
1853
1854        Some((s, e))
1855    }
1856}
1857
1858// Allow slicing directly from PieceTree.
1859impl PieceTree {
1860    #[inline]
1861    pub fn slice<R: RangeBounds<u32>>(&self, range: R) -> TreeSlice<'_> {
1862        let s = match range.start_bound() {
1863            Bound::Included(&n) => n,
1864            Bound::Excluded(&n) => n + 1,
1865            Bound::Unbounded    => 0,
1866        };
1867        let e = match range.end_bound() {
1868            Bound::Included(&n) => n + 1,
1869            Bound::Excluded(&n) => n,
1870            Bound::Unbounded    => self.len_bytes(),
1871        };
1872        TreeSlice::new(self, s, e)
1873    }
1874
1875    /// Slice by char range.
1876    #[inline]
1877    pub fn slice_chars_range<R: RangeBounds<u32>>(&self, range: R) -> TreeSlice<'_> {
1878        let sc = match range.start_bound() {
1879            Bound::Included(&n) => n,
1880            Bound::Excluded(&n) => n + 1,
1881            Bound::Unbounded    => 0,
1882        };
1883        let ec = match range.end_bound() {
1884            Bound::Included(&n) => n + 1,
1885            Bound::Excluded(&n) => n,
1886            Bound::Unbounded    => self.len_chars(),
1887        };
1888        let s = self.char_to_byte(sc).unwrap_or(0);
1889        let e = self.char_to_byte(ec).unwrap_or_else(|| self.len_bytes());
1890        TreeSlice::new(self, s, e)
1891    }
1892}
1893
1894impl PieceTree {
1895    /// Fast-path chunk reader specifically designed for the Tree-sitter C API.
1896    /// Given an absolute byte offset, it returns the largest contiguous byte
1897    /// slice available starting exactly at that offset.
1898    #[inline]
1899    #[must_use]
1900    pub fn read_largest_contigous_chunk_at_byte(&self, offset: u32) -> &[u8] {
1901        let total = self.total_length();
1902        if offset >= total {
1903            return &[];
1904        }
1905
1906        let mut current = self.root;
1907        let mut current_offset = offset;
1908
1909        while current != NIL {
1910            let node = self.pieces.get(current);
1911            let p = self.pieces.get_piece(current);
1912            let left_len = self.pieces.get(node.left).subtree_len;
1913            let piece_len = p.byte_length;
1914
1915            if current_offset < left_len {
1916                current = node.left;
1917
1918            } else if current_offset < left_len + piece_len {
1919                //
1920                // The requested offset falls inside this exact piece
1921                //
1922
1923                let rel_offset = current_offset - left_len;
1924                let text = self.buffers.get_slice(p.buffer, p.byte_offset, p.byte_length);
1925
1926                return &text.as_bytes()[rel_offset as usize..];
1927
1928            } else {
1929                current_offset -= left_len + piece_len;
1930                current = node.right;
1931            }
1932        }
1933
1934        &[]
1935    }
1936
1937    #[inline]
1938    #[must_use]
1939    pub fn line_to_byte(&self, target_line: u32) -> Option<u32> {
1940        if target_line == 0 { return Some(0) }
1941        let mut current = self.root;
1942        let mut current_offset = 0;
1943        let mut current_line = 0;
1944        while current != NIL {
1945            let node = self.pieces.get(current);
1946            let p = self.pieces.get_piece(current);
1947            let left_newlines = self.pieces.get(node.left).subtree_newlines;
1948            let left_len      = self.pieces.get(node.left).subtree_len;
1949            let piece_newlines = p.newline_count;
1950            let piece_len      = p.byte_length;
1951
1952            if target_line <= current_line + left_newlines {
1953                //
1954                // The target line starts somewhere in the left subtree
1955                //
1956                current = node.left;
1957
1958            } else if target_line <= current_line + left_newlines + piece_newlines {
1959                //
1960                // The target line starts inside this piece
1961                //
1962
1963                current_line   += left_newlines;
1964                current_offset += left_len;
1965
1966                let rel_line = target_line - current_line; // >= 1, guaranteed
1967                let absolute_newline_index = p.buffer_start_line + rel_line - 1;
1968                let absolute_byte_offset = if p.buffer == MOD_BUFFER {
1969                    self.buffers.modifications_newline_offsets[absolute_newline_index as usize]
1970                } else {
1971                    self.buffers.original_buffers[p.buffer].newline_offsets[absolute_newline_index as usize]
1972                };
1973
1974                let local_piece_offset = absolute_byte_offset - p.byte_offset + 1;
1975                return Some(current_offset + local_piece_offset);
1976
1977            } else {
1978                current_line   += left_newlines + piece_newlines;
1979                current_offset += left_len + piece_len;
1980                current = node.right;
1981            }
1982        }
1983
1984        None
1985    }
1986
1987    #[inline]
1988    #[must_use]
1989    pub fn char_to_byte(&self, char_index: u32) -> Option<u32> {
1990        let total_chars = self.len_chars();
1991        if char_index > total_chars { return None; }
1992        if char_index == total_chars { return Some(self.len_bytes()); }
1993
1994        let mut current = self.root;
1995        let mut current_byte = 0;
1996        let mut current_char = 0;
1997
1998        while current != NIL {
1999            let node = self.pieces.get(current);
2000            let left_node = self.pieces.get(node.left);
2001
2002            let left_chars = left_node.subtree_chars;
2003            let left_bytes = left_node.subtree_len;
2004
2005            let p = self.pieces.get_piece(current);
2006            let piece_chars = p.char_count;
2007
2008            if char_index < current_char + left_chars {
2009                current = node.left;
2010
2011            } else if char_index < current_char + left_chars + piece_chars {
2012                //
2013                // Target char is inside this piece,
2014                // p.piece_start_char is the absolute char index of p.offset in its buffer,
2015                // so (p.piece_start_char + rel_char) is the absolute char target.
2016                //
2017
2018                let rel_char = char_index - (current_char + left_chars);
2019                let absolute_target_byte =
2020                    self.buffers.char_to_byte_absolute(p.buffer, p.piece_start_char + rel_char);
2021
2022                return Some(current_byte + left_bytes + (absolute_target_byte - p.byte_offset));
2023
2024            } else {
2025                current_char += left_chars + piece_chars;
2026                current_byte += left_bytes + p.byte_length;
2027                current = node.right;
2028            }
2029        }
2030
2031        None
2032    }
2033
2034    #[inline]
2035    #[must_use]
2036    pub fn byte_to_char(&self, byte_offset: u32) -> Option<u32> {
2037        let total_bytes = self.len_bytes();
2038        if byte_offset > total_bytes { return None; }
2039        if byte_offset == total_bytes { return Some(self.len_chars()); }
2040
2041        let mut current = self.root;
2042        let mut current_byte = 0;
2043        let mut current_char = 0;
2044
2045        while current != NIL {
2046            let node = self.pieces.get(current);
2047            let left_node = self.pieces.get(node.left);
2048
2049            let left_bytes = left_node.subtree_len;
2050            let left_chars = left_node.subtree_chars;
2051
2052            let p = self.pieces.get_piece(current);
2053            let piece_bytes = p.byte_length;
2054
2055            if byte_offset < current_byte + left_bytes {
2056                current = node.left;
2057
2058            } else if byte_offset < current_byte + left_bytes + piece_bytes {
2059                // Target byte is inside this piece.
2060                // byte_to_char_absolute gives the absolute char index, so subtracting
2061                // piece_start_char converts it to a piece-relative char count.
2062
2063                let rel_byte = byte_offset - (current_byte + left_bytes);
2064                let absolute_target_char =
2065                    self.buffers.byte_to_char_absolute(p.buffer, p.byte_offset + rel_byte);
2066
2067                return Some(current_char + left_chars + (absolute_target_char - p.piece_start_char));
2068
2069            } else {
2070                current_byte += left_bytes + piece_bytes;
2071                current_char += left_chars + p.char_count;
2072                current = node.right;
2073            }
2074        }
2075
2076        None
2077    }
2078
2079    #[inline]
2080    #[must_use]
2081    pub fn byte_to_line_col(&self, offset: u32) -> Option<(u32, u32)> {
2082        let total_bytes = self.len_bytes();
2083        if offset > total_bytes { return None }
2084        if offset == total_bytes {
2085            //
2086            // Get offset of the last line
2087            //
2088            let line            = self.pieces.get(self.root).subtree_newlines;
2089            let line_start_byte = self.line_to_byte(line).unwrap_or(0);
2090            let target_char     = self.len_chars();
2091            let line_start_char = self.byte_to_char(line_start_byte)?;
2092            return Some((line, target_char - line_start_char));
2093        }
2094
2095        let mut current         = self.root;
2096        let mut current_line    = 0u32;
2097        let mut current_byte    = 0u32;
2098        let mut current_char    = 0u32;
2099
2100        while current != NIL {
2101            let node          = self.pieces.get(current);
2102            let left_node     = self.pieces.get(node.left);
2103            let left_bytes    = left_node.subtree_len;
2104            let left_newlines = left_node.subtree_newlines;
2105            let left_chars    = left_node.subtree_chars;
2106            let p             = self.pieces.get_piece(current);
2107            let piece_bytes   = p.byte_length;
2108
2109            if offset < current_byte + left_bytes {
2110                current = node.left;
2111
2112            } else if offset < current_byte + left_bytes + piece_bytes {
2113                current_line += left_newlines;
2114                current_char += left_chars;
2115
2116                let rel_byte = offset - (current_byte + left_bytes);
2117
2118                let (local_newlines, col) = if p.newline_count == 0 {
2119                    let target_char = self.buffers.byte_to_char_absolute(p.buffer, p.byte_offset + rel_byte);
2120                    let rel_char    = target_char - p.piece_start_char;
2121                    let abs_char    = current_char + rel_char;
2122
2123                    let line_start_byte = self.line_to_byte(current_line)?;
2124                    let line_start_char = self.byte_to_char(line_start_byte)?;
2125
2126                    (0, abs_char - line_start_char)
2127                } else {
2128                    let start_index       = p.buffer_start_line as usize;
2129                    let end_index         = start_index + p.newline_count as usize;
2130                    let absolute_target = p.byte_offset + rel_byte;
2131
2132                    let offsets = &self.buffers.get_newlines(p.buffer)[start_index..end_index];
2133
2134                    let local_nl = offsets.partition_point(|&off| off < absolute_target) as u32;
2135
2136                    let target_char = self.buffers.byte_to_char_absolute(p.buffer, p.byte_offset + rel_byte);
2137                    let col = if local_nl > 0 {
2138                        let last_nl_abs_byte = offsets[local_nl as usize - 1];
2139                        let last_nl_char     = self.buffers.byte_to_char_absolute(p.buffer, last_nl_abs_byte);
2140                        target_char - (last_nl_char + 1)
2141                    } else {
2142                        let line_start_byte = self.line_to_byte(current_line)?;
2143                        let line_start_char = self.byte_to_char(line_start_byte)?;
2144                        let rel_char        = target_char - p.piece_start_char;
2145                        (current_char + rel_char) - line_start_char
2146                    };
2147
2148                    (local_nl, col)
2149                };
2150
2151                return Some((current_line + local_newlines, col));
2152
2153            } else {
2154                current_line += left_newlines + p.newline_count;
2155                current_byte += left_bytes + piece_bytes;
2156                current_char += left_chars + p.char_count;
2157                current = node.right;
2158            }
2159        }
2160
2161        None
2162    }
2163
2164    #[inline]
2165    #[must_use]
2166    pub fn pieces(&self) -> PieceTreeIter<'_> {
2167        PieceTreeIter::new(&self.pieces, self.root)
2168    }
2169
2170    #[inline]
2171    #[must_use]
2172    pub fn get_line_range(&self, line: u32) -> Option<(u32, u32)> {
2173        let start = self.line_to_byte(line)?;
2174        let end = self.line_to_byte(line + 1).unwrap_or_else(|| self.total_length());
2175        Some((start, end))
2176    }
2177
2178    #[inline]
2179    #[must_use]
2180    pub fn get_line_content_allocating(&self, line: u32) -> Option<String> {
2181        let (start, end) = self.get_line_range(line)?;
2182
2183        let mut content = String::with_capacity((end - start) as usize);
2184        let mut walker = TreeWalker::new(self);
2185
2186        walker.seek(start);
2187        while walker.offset < end {
2188            if let Some(c) = walker.next() {
2189                content.push(c);
2190            } else {
2191                break;
2192            }
2193        }
2194
2195        Some(content)
2196    }
2197
2198        /// Returns an iterator of &str chunks over the given byte range.
2199    /// Zero allocation.
2200    #[inline]
2201    #[must_use]
2202    pub fn chunks_at_byte(&self, start: u32, end: u32) -> ChunkIter<'_> {
2203        let end = end.min(self.total_length());
2204        ChunkIter::new(self, start, end)
2205    }
2206
2207    /// Byte at a given byte offset. O(log n).
2208    #[inline]
2209    #[must_use]
2210    pub fn byte(&self, offset: u32) -> Option<u8> {
2211        let (node, rel) = self.find_position(offset, false)?;
2212        let p = self.get_piece(node);
2213        let text = self.buffers.get_slice(p.buffer, p.byte_offset, p.byte_length);
2214        text.as_bytes().get(rel as usize).copied()
2215    }
2216
2217    /// Char at a given char index. O(log n) to find the piece, then
2218    /// a short scan within the piece.
2219    #[inline]
2220    #[must_use]
2221    pub fn char(&self, char_index: u32) -> Option<char> {
2222        let byte_offset = self.char_to_byte(char_index)?;
2223        let (node, rel) = self.find_position(byte_offset, false)?;
2224        let p = self.get_piece(node);
2225        let text = self.buffers.get_slice(p.buffer, p.byte_offset, p.byte_length);
2226        text[rel as usize..].chars().next()
2227    }
2228
2229    /// Returns a non-allocating iterator of chars over the given char range.
2230    /// Backed by `TreeWalker::seek` so it reuses existing infrastructure.
2231    #[inline]
2232    #[must_use]
2233    pub fn slice_chars(&self, char_start: u32, char_end: u32) -> SliceChars<'_> {
2234        let byte_start = self.char_to_byte(char_start).unwrap_or(0);
2235        let byte_end   = self.char_to_byte(char_end).unwrap_or_else(|| self.total_length());
2236        SliceChars {
2237            walker:   { let mut w = TreeWalker::new(self); w.seek(byte_start); w },
2238            byte_end,
2239        }
2240    }
2241
2242    /// Returns a non-allocating iterator of chars over the given byte range.
2243    #[inline]
2244    #[must_use]
2245    pub fn slice_bytes(&self, byte_start: u32, byte_end: u32) -> SliceChars<'_> {
2246        SliceChars {
2247            walker:   { let mut w = TreeWalker::new(self); w.seek(byte_start); w },
2248            byte_end,
2249        }
2250    }
2251
2252    /// Non-allocating line view: returns a `ChunkIter` over the byte range of
2253    /// `line`. Line numbers are 0-based. The trailing \n is included if present.
2254    #[inline]
2255    #[must_use]
2256    pub fn line(&self, line: u32) -> Option<ChunkIter<'_>> {
2257        let start = self.line_to_byte(line)?;
2258        let end   = self.line_to_byte(line + 1)
2259                        .unwrap_or_else(|| self.total_length());
2260        Some(self.chunks_at_byte(start, end))
2261    }
2262
2263    /// Number of lines (= newline count + 1)
2264    #[inline]
2265    #[must_use]
2266    pub fn len_lines(&self) -> u32 { self.pieces.get(self.root).subtree_newlines + 1 }
2267
2268    #[inline(always)]
2269    #[must_use]
2270    pub fn len_chars(&self) -> u32 { self.pieces.get(self.root).subtree_chars }
2271
2272    #[inline]
2273    #[must_use]
2274    pub fn len_bytes(&self) -> u32 { self.total_length() }
2275
2276    #[inline]
2277    #[must_use]
2278    pub fn chars(&self) -> TreeWalker<'_> { TreeWalker::new(self) }
2279
2280    #[inline(always)]
2281    #[must_use]
2282    pub fn chars_rev(&self) -> ReverseTreeWalker<'_> { ReverseTreeWalker::new(self) }
2283
2284    #[inline]
2285    #[must_use]
2286    pub fn lines(&self) -> LinesIter<'_> {
2287        LinesIter { tree: self, current_line: 0, total_lines: self.len_lines() }
2288    }
2289
2290    #[inline]
2291    pub fn remove<R>(&mut self, range: R) where R: RangeBounds<u32> {
2292        let start = match range.start_bound() {
2293            Bound::Included(&n) => n,
2294            Bound::Excluded(&n) => n + 1,
2295            Bound::Unbounded => 0,
2296        };
2297        let end = match range.end_bound() {
2298            Bound::Included(&n) => n + 1,
2299            Bound::Excluded(&n) => n,
2300            Bound::Unbounded => self.len_bytes(),
2301        };
2302        if end > start { self.remove_at(start, end - start) }
2303    }
2304
2305    #[inline]
2306    #[must_use]
2307    pub fn char_to_line(&self, char_index: u32) -> Option<u32> {
2308        let byte_index = self.char_to_byte(char_index)?;
2309        self.byte_to_line_col(byte_index).map(|(line, _)| line)
2310    }
2311
2312    #[inline]
2313    #[must_use]
2314    pub fn char_to_line_col(&self, char_index: u32) -> Option<(u32, u32)> {
2315        let byte_index = self.char_to_byte(char_index)?;
2316        self.byte_to_line_col(byte_index)
2317    }
2318
2319    #[inline]
2320    #[must_use]
2321    pub fn line_to_char(&self, line: u32) -> Option<u32> {
2322        let byte_index = self.line_to_byte(line)?;
2323        self.byte_to_char(byte_index)
2324    }
2325}
2326
2327#[derive(Debug)]
2328pub struct PieceTreeIter<'a, const MAX_INLINE_TREE_DEPTH: usize = 32> {
2329    arena: &'a Pieces,
2330    stack: SmallVec<[NodeRef; MAX_INLINE_TREE_DEPTH]>,
2331}
2332
2333impl<'a, const MAX_INLINE_TREE_DEPTH: usize> PieceTreeIter<'a, MAX_INLINE_TREE_DEPTH> {
2334    #[inline]
2335    #[must_use]
2336    pub fn new(arena: &'a Pieces, mut root: NodeRef) -> Self {
2337        let mut stack = SmallVec::new();
2338        while root != NIL {
2339            stack.push(root);
2340            root = arena.get(root).left;
2341        }
2342
2343        Self { arena, stack }
2344    }
2345}
2346
2347impl<const MAX_INLINE_TREE_DEPTH: usize> Iterator for PieceTreeIter<'_, MAX_INLINE_TREE_DEPTH> {
2348    type Item = (NodeRef, Piece);
2349
2350    #[inline]
2351    fn next(&mut self) -> Option<Self::Item> {
2352        let node_index = self.stack.pop()?;
2353        let node = self.arena.get(node_index);
2354        let p = self.arena.get_piece(node_index);
2355
2356        let mut current = node.right;
2357        while current != NIL {
2358            self.stack.push(current);
2359            current = self.arena.get(current).left;
2360        }
2361
2362        Some((node_index, p))
2363    }
2364}
2365
2366#[derive(Clone, Copy, PartialEq, Debug)]
2367enum Direction { Left, Center, Right }
2368
2369#[derive(Debug)]
2370pub struct TreeWalker<'a, const MAX_INLINE_TREE_DEPTH: usize = 32> {
2371    tree: &'a PieceTree,
2372    stack: SmallVec<[(NodeRef, Direction); MAX_INLINE_TREE_DEPTH]>,
2373    current_str: str::Chars<'a>,
2374    pub offset: u32,
2375}
2376
2377impl<'a, const MAX_INLINE_TREE_DEPTH: usize> TreeWalker<'a, MAX_INLINE_TREE_DEPTH> {
2378    #[inline]
2379    #[must_use]
2380    pub fn new(tree: &'a PieceTree) -> Self {
2381        let mut walker = Self {
2382            tree,
2383            stack: SmallVec::new(),
2384            current_str: "".chars(),
2385            offset: 0,
2386        };
2387        if tree.root != NIL {
2388            walker.stack.push((tree.root, Direction::Left));
2389        }
2390        walker.populate_chars();
2391        walker
2392    }
2393
2394    #[inline]
2395    pub fn seek(&mut self, target: u32) {
2396        self.stack.clear();
2397        self.offset = target;
2398        self.current_str = "".chars();
2399
2400        if self.tree.root == NIL { return; }
2401
2402        let mut current = self.tree.root;
2403        let mut current_offset = target;
2404
2405        while current != NIL {
2406            let node = self.tree.pieces.get(current);
2407            let p = self.tree.pieces.get_piece(current);
2408            let left_len = self.tree.pieces.get(node.left).subtree_len;
2409            let piece_len = p.byte_length;
2410
2411            if current_offset < left_len {
2412                self.stack.push((current, Direction::Center));
2413                current = node.left;
2414            } else if current_offset < left_len + piece_len {
2415                self.stack.push((current, Direction::Right));
2416                let text = self.tree.buffers.get_slice(p.buffer, p.byte_offset, p.byte_length);
2417                let rel_offset = current_offset - left_len;
2418                self.current_str = text[rel_offset as usize..].chars();
2419                break;
2420            } else {
2421                current_offset -= left_len + piece_len;
2422                current = node.right;
2423            }
2424        }
2425    }
2426
2427    #[inline]
2428    fn populate_chars(&mut self) {
2429        while let Some((node_index, dir)) = self.stack.pop() {
2430            let node = self.tree.pieces.get(node_index);
2431            match dir {
2432                Direction::Left => {
2433                    self.stack.push((node_index, Direction::Center));
2434                    if node.left != NIL { self.stack.push((node.left, Direction::Left)); }
2435                }
2436
2437                Direction::Center => {
2438                    self.stack.push((node_index, Direction::Right));
2439                    let p = self.tree.pieces.get_piece(node_index);
2440                    let text = self.tree.buffers.get_slice(p.buffer, p.byte_offset, p.byte_length);
2441                    self.current_str = text.chars();
2442                    return;
2443                }
2444
2445                Direction::Right => {
2446                    if node.right != NIL { self.stack.push((node.right, Direction::Left)); }
2447                }
2448            }
2449        }
2450    }
2451}
2452
2453impl Iterator for TreeWalker<'_> {
2454    type Item = char;
2455
2456    #[inline(always)]
2457    fn next(&mut self) -> Option<char> {
2458        loop {
2459            if let Some(c) = self.current_str.next() {
2460                self.offset += c.len_utf8() as u32;
2461                return Some(c);
2462            }
2463
2464            if self.stack.is_empty() { return None; }
2465            self.populate_chars();
2466        }
2467    }
2468}
2469
2470#[derive(Debug)]
2471pub struct ReverseTreeWalker<'a, const MAX_INLINE_TREE_DEPTH: usize = 32> {
2472    tree: &'a PieceTree,
2473    stack: SmallVec<[(NodeRef, bool); MAX_INLINE_TREE_DEPTH]>,
2474    current_str: str::Chars<'a>,
2475}
2476
2477impl<'a, const MAX_INLINE_TREE_DEPTH: usize> ReverseTreeWalker<'a, MAX_INLINE_TREE_DEPTH> {
2478    #[inline(always)]
2479    #[must_use]
2480    pub fn new(tree: &'a PieceTree) -> Self {
2481        let mut walker = Self {
2482            tree,
2483            stack: SmallVec::new(),
2484            current_str: "".chars(),
2485        };
2486        walker.push_rightmost(tree.root);
2487        walker
2488    }
2489
2490    #[inline(always)]
2491    fn push_rightmost(&mut self, mut node: NodeRef) {
2492        while node != NIL {
2493            self.stack.push((node, false));
2494            node = self.tree.pieces.get(node).right;
2495        }
2496    }
2497
2498    #[inline]
2499    pub fn seek(&mut self, mut target_offset: u32) {
2500        self.stack.clear();
2501        self.current_str = "".chars();
2502
2503        if self.tree.root == NIL { return; }
2504
2505        let total_bytes = self.tree.len_bytes();
2506
2507        if target_offset > total_bytes {
2508            target_offset = total_bytes;
2509        }
2510        if target_offset == total_bytes {
2511            self.push_rightmost(self.tree.root);
2512            return;
2513        }
2514
2515        let mut current = self.tree.root;
2516        let mut current_offset = target_offset;
2517
2518        while current != NIL {
2519            let node = self.tree.pieces.get(current);
2520            let p = self.tree.pieces.get_piece(current);
2521            let left_len = self.tree.pieces.get(node.left).subtree_len;
2522            let piece_len = p.byte_length;
2523
2524            if current_offset < left_len {
2525                self.stack.push((current, true));
2526                current = node.left;
2527
2528            } else if current_offset < left_len + piece_len {
2529                self.stack.push((current, true));
2530
2531                let text = self.tree.buffers.get_slice(p.buffer, p.byte_offset, p.byte_length);
2532                let rel_offset = current_offset - left_len;
2533
2534                // Keep the subset of the string before the offset
2535                self.current_str = text[..rel_offset as usize].chars();
2536
2537                break;
2538
2539            } else {
2540                self.stack.push((current, false));
2541                current_offset -= left_len + piece_len;
2542                current = node.right;
2543            }
2544        }
2545    }
2546}
2547
2548impl Iterator for ReverseTreeWalker<'_> {
2549    type Item = char;
2550
2551    #[inline]
2552    fn next(&mut self) -> Option<Self::Item> {
2553        if let Some(c) = self.current_str.next_back() { return Some(c); }
2554
2555        while let Some((node_index, visited_right)) = self.stack.pop() {
2556            let node = self.tree.pieces.get(node_index);
2557            if visited_right {
2558                self.push_rightmost(node.left);
2559            } else {
2560                self.stack.push((node_index, true));
2561
2562                let p = self.tree.pieces.get_piece(node_index);
2563                let text_slice = self.tree.buffers.get_slice(p.buffer, p.byte_offset, p.byte_length);
2564
2565                self.current_str = text_slice.chars();
2566
2567                if let Some(c) = self.current_str.next_back() { return Some(c); }
2568            }
2569        }
2570
2571        None
2572    }
2573}
2574
2575impl PieceTree {
2576    #[inline]
2577    pub fn debug(&self, f: &mut impl core::fmt::Write) -> core::fmt::Result {
2578        writeln!(f, "\n--- Tree State (Root: {:?}) ---", self.root)?;
2579        self.print_inorder(f, self.root, &mut None, 0)?;
2580        writeln!(f, "------------------------------\n")
2581    }
2582
2583    fn print_inorder(&self, f: &mut impl core::fmt::Write, node: NodeRef, last: &mut Option<Piece>, depth: usize) -> core::fmt::Result {
2584        if node == NIL { return Ok(()) }
2585
2586        let n = self.pieces.nodes[node];
2587
2588        self.print_inorder(f, n.left, last, depth + 1)?;
2589
2590        let cur = self.get_piece(node);
2591
2592        //
2593        // Check for the mergeable invariant
2594        //
2595        let warning = if let Some(prev) = last {
2596            if prev.buffer == cur.buffer
2597               && prev.byte_offset + prev.byte_length == cur.byte_offset
2598            {
2599                " --------- [!!! MERGEABLE NEIGHBORS NOT MERGED !!!] ---------"
2600            } else {
2601                ""
2602            }
2603        } else {
2604            ""
2605        };
2606
2607        let Piece { buffer, byte_offset: offset, byte_length: length, .. } = cur;
2608
2609        writeln!(
2610            f,
2611            "{:indent$}Node {node:?}: Buf={}, Off={offset}, Len={length}{warning}",
2612            "", buffer.as_u32(), indent = depth * 4
2613        )?;
2614
2615        *last = Some(cur);
2616
2617        self.print_inorder(f, n.right, last, depth + 1)
2618    }
2619}
2620
2621pub fn assert_state(tree: &PieceTree, expected: &str) {
2622    let tree_text = tree.to_string();
2623
2624    assert_eq!(tree_text, expected, "Text mismatch");
2625
2626    if !expected.is_empty() {
2627        let offsets = [0, expected.len() / 2, expected.len() - 1];
2628        for off in offsets {
2629            let chunk = tree.read_largest_contigous_chunk_at_byte(off as u32);
2630            let chunk_str = str::from_utf8(chunk).unwrap();
2631            assert!(
2632                expected[off..].starts_with(chunk_str),
2633                "Chunk mismatch at offset {}",
2634                off
2635            );
2636        }
2637    }
2638}
2639
2640pub fn assert_invariants(tree: &PieceTree) {
2641    fn check(tree: &PieceTree, node: NodeRef) -> (usize, usize) {
2642        if node == NIL { return (0, 1) }
2643
2644        let n = tree.pieces.nodes[node];
2645        let piece = tree.get_piece(node);
2646
2647        let (l_len, l_bh) = check(tree, n.left);
2648        let (r_len, r_bh) = check(tree, n.right);
2649
2650        let expected_len = l_len + piece.byte_length as usize + r_len;
2651        assert_eq!(n.subtree_len as usize, expected_len, "subtree_len mismatch");
2652
2653        if n.color() == Color::Red {
2654            assert_eq!(
2655                tree.pieces.nodes[n.left].color(),
2656                Color::Black,
2657                "red node with red left child"
2658            );
2659            assert_eq!(
2660                tree.pieces.nodes[n.right].color(),
2661                Color::Black,
2662                "red node with red right child"
2663            );
2664        }
2665
2666        assert_eq!(l_bh, r_bh, "black height mismatch");
2667
2668        let bh = l_bh + if n.color() == Color::Black { 1 } else { 0 };
2669        (expected_len, bh)
2670    }
2671
2672    if tree.root != NIL {
2673        assert_eq!(
2674            tree.pieces.nodes[tree.root].color(),
2675            Color::Black,
2676            "root is not black"
2677        );
2678        let (len, _) = check(tree, tree.root);
2679        assert_eq!(len, tree.total_length() as usize, "Tree length and root length differ");
2680
2681    } else {
2682        assert_eq!(tree.total_length(), 0,            "Empty tree has nonzero length");
2683    }
2684}
2685
2686pub fn assert_piece_metadata(tree: &PieceTree) {
2687    fn collect(tree: &PieceTree, node: NodeRef, out: &mut Vec<Piece>) {
2688        if node == NIL { return }
2689
2690        let n = tree.pieces.nodes[node];
2691
2692        collect(tree, n.left, out);
2693        out.push(tree.pieces.get_piece(node));
2694        collect(tree, n.right, out);
2695    }
2696
2697    let mut pieces = Vec::new();
2698    collect(tree, tree.root, &mut pieces);
2699
2700    for p in &pieces {
2701        let buf   = tree.buffers.get(p.buffer);
2702        let start = p.byte_offset as usize;
2703        let end   = start + p.byte_length as usize;
2704
2705        assert!(end <= buf.len(), "Piece points past end of buffer");
2706        assert!(p.byte_length > 0, "Zero-length piece found");
2707
2708        let slice = &buf[start..end];
2709        let (actual_chars, actual_nl) = count_chars_and_newlines(slice.as_bytes());
2710        assert_eq!(p.char_count,    actual_chars, "char_count mismatch");
2711        assert_eq!(p.newline_count, actual_nl,    "newline_count mismatch");
2712
2713        //
2714        // piece_start_char and buffer_start_line: scan only the prefix once
2715        //
2716        let prefix = &buf.as_bytes()[..p.byte_offset as usize];
2717        let actual_start_char = bytecount::num_chars(prefix) as u32;
2718        let actual_start_line = prefix.iter().filter(|&&b| b == b'\n').count() as u32;
2719
2720        assert_eq!(p.piece_start_char,  actual_start_char,
2721            "piece_start_char mismatch buffer={} offset={}", p.buffer.as_u32(), p.byte_offset);
2722
2723        assert_eq!(p.buffer_start_line, actual_start_line,
2724            "buffer_start_line mismatch buffer={} offset={}", p.buffer.as_u32(), p.byte_offset);
2725
2726        //
2727        // Verify the newline_offsets slice
2728        //
2729        let nl_offsets = &tree.buffers.get_newlines(p.buffer)
2730            [p.buffer_start_line as usize..(p.buffer_start_line + p.newline_count) as usize];
2731
2732        for (i, &abs_byte) in nl_offsets.iter().enumerate() {
2733            assert!(
2734                abs_byte >= p.byte_offset && abs_byte < p.byte_offset + p.byte_length,
2735                "newline_offsets[{}] outside piece", p.buffer_start_line as usize + i
2736            );
2737
2738            assert_eq!(
2739                buf.as_bytes()[abs_byte as usize], b'\n',
2740                "newline_offsets[{}] not a newline", p.buffer_start_line as usize + i
2741            );
2742        }
2743    }
2744}
2745
2746pub fn assert_no_mergeable_neighbors(tree: &PieceTree) {
2747    fn inorder(tree: &PieceTree, node: NodeRef, last: &mut Option<Piece>) {
2748        if node == NIL { return }
2749
2750        let n = tree.pieces.nodes[node];
2751        inorder(tree, n.left, last);
2752
2753        let cur = tree.get_piece(node);
2754
2755        if let Some(prev) = *last {
2756            if prev.buffer == cur.buffer
2757                && prev.byte_offset + prev.byte_length == cur.byte_offset
2758            {
2759                panic!("Mergeable neighboring pieces were left unmerged");
2760            }
2761        }
2762
2763        *last = Some(cur);
2764        inorder(tree, n.right, last);
2765    }
2766
2767    let mut last = None;
2768    inorder(tree, tree.root, &mut last);
2769}
2770
2771pub fn assert_coordinates(tree: &PieceTree, oracle: &str) {
2772    #[inline]
2773    fn oracle_offset_to_line_col(s: &str, byte_offset: usize) -> (usize, usize) {
2774        let slice = &s[..byte_offset];
2775        let line  = bytecount::count(slice.as_bytes(), b'\n');
2776        let last_nl = slice.rfind('\n').map(|i| i + 1).unwrap_or(0);
2777        let col   = bytecount::num_chars(s[last_nl..byte_offset].as_bytes());
2778        (line, col)
2779    }
2780
2781    #[inline]
2782    fn oracle_line_to_offset(s: &str, target_line: usize) -> usize {
2783        if target_line == 0 { return 0 }
2784
2785        let mut line = 0;
2786        for (i, c) in s.char_indices() {
2787            if c == '\n' {
2788                line += 1;
2789                if line == target_line { return i + 1; }
2790            }
2791        }
2792
2793        s.len()
2794    }
2795
2796    fn check_coordinate(tree: &PieceTree, oracle: &str, byte_index: u32) {
2797        let char_index = bytecount::num_chars(oracle[..byte_index as usize].as_bytes()) as u32;
2798
2799        assert_eq!(tree.char_to_byte(char_index), Some(byte_index),
2800                   "char_to_byte({}) wrong", char_index);
2801
2802        assert_eq!(tree.byte_to_char(byte_index), Some(char_index),
2803                   "byte_to_char({}) wrong", byte_index);
2804
2805        let expected_lc = oracle_offset_to_line_col(oracle, byte_index as usize);
2806        assert_eq!(
2807            tree.byte_to_line_col(byte_index),
2808            Some((expected_lc.0 as u32, expected_lc.1 as u32)),
2809            "offset_to_line_col({}) wrong", byte_index
2810        );
2811    }
2812
2813    let total_bytes = oracle.len() as u32;
2814    let total_chars = oracle.chars().count() as u32;
2815
2816    //
2817    // Always check these
2818    //
2819    check_coordinate(tree, oracle, 0);
2820    if total_bytes > 0 {
2821        check_coordinate(tree, oracle, total_bytes - 1);
2822        check_coordinate(tree, oracle, total_bytes / 2);
2823    }
2824
2825
2826    //
2827    // Sample ~8 positions spread across the document
2828    //
2829    for i in 0u32..8 {
2830        let byte = ((total_bytes as u64 * ((i as u64 * 2654435761) & 0xFFFFFFFF)) >> 32) as u32 % total_bytes.max(1);
2831
2832        // Snap to char boundary
2833        let byte = oracle.as_bytes()[..byte as usize]
2834            .iter().rposition(|&b| b < 0x80 || b >= 0xC0)
2835            .map(|i| i as u32)
2836            .unwrap_or(0);
2837
2838        check_coordinate(tree, oracle, byte);
2839    }
2840
2841    //
2842    // EOF and out-of-bounds always
2843    //
2844    assert_eq!(tree.char_to_byte(total_chars), Some(total_bytes));
2845    assert_eq!(tree.byte_to_char(total_bytes), Some(total_chars));
2846    assert_eq!(tree.char_to_byte(total_chars + 1), None);
2847    assert_eq!(tree.byte_to_char(total_bytes + 1), None);
2848    assert_eq!(tree.byte_to_line_col(total_bytes + 1), None);
2849
2850    //
2851    // line_to_offset: check first, last, middle line only
2852    //
2853    let total_lines = oracle.chars().filter(|&c| c == '\n').count() + 1;
2854    for line in [0, total_lines / 2, total_lines.saturating_sub(1)] {
2855        let expected = oracle_line_to_offset(oracle, line) as u32;
2856        assert_eq!(tree.line_to_byte(line as u32), Some(expected),
2857            "line_to_offset({}) wrong", line);
2858    }
2859
2860    assert_eq!(tree.line_to_byte(total_lines as u32), None);
2861}