1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! Regression guards for the per-keystroke cost of editing large
//! rope-clean documents.
//!
//! Three independent O(N) costs used to ride on every keystroke in a
//! 1000-block document:
//! 1. `BlockOffsetIndex::shift_after` scanned ALL entries even when
//! the threshold was past the end (fixed: `partition_point`).
//! 2. The snapshot taken for undo memcpy'd the whole entries Vec
//! (fixed: `Arc<Vec>` + copy-on-write — the clone is skipped when
//! no entry actually shifts).
//! 3. `insert_text_uc` / `delete_text_uc` rewrote
//! `Block.document_position` on every block after the cursor
//! (fixed: gated behind `rope_positions_match_flow`, so rope-clean
//! docs derive positions from the index instead).
//!
//! For inserts at the END of a document, none of those three need to
//! touch any trailing entry. The per-keystroke cost drops from
//! linear-in-block-count to a much smaller residual (rope-size log
//! factors, the im::HashMap marker-index lookups, and the per-edit
//! UoW commit/snapshot constants). That large reduction is the signal
//! this test guards: a 10x larger document must cost only modestly
//! more per end-insert (~4x in practice), NOT the ~8-10x it cost when
//! a per-block position-refresh walk rode on every keystroke.
//!
//! (Insert-at-START is intrinsically O(N) — every entry's byte offset
//! genuinely shifts — so it is deliberately not used as the guard;
//! only a Fenwick/segment-tree rewrite of `shift_after` could make it
//! sub-linear. The `cursor` is created once outside the timed loop
//! because `cursor_at` triggers an O(N) `get_document_stats`
//! word-count via grapheme snapping.)
use black_box;
use ;
use TextDocument;
const PARAGRAPH: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
/// Measure `n_inserts` single-char insertions at the END of the
/// document. A single cursor is created once (outside the timed loop)
/// and reused — `cursor_at` itself triggers an O(N) `get_document_stats`
/// word-count via grapheme snapping, which would otherwise dominate
/// the measurement and mask the insert cost we care about. The cursor
/// auto-advances to the new end after each insert.
/// Inserting one char at the end of a 10x larger rope-clean document
/// must cost only modestly more per keystroke (~4x in practice from
/// rope-size log factors and commit overhead), NOT the ~8-10x it cost
/// when a per-block position-refresh walk rode on every keystroke. A
/// ratio past 6x means an O(N) walk has crept back into the end-insert
/// path — check that shift_after still short-circuits when no entries
/// shift, that the snapshot Arc<Vec> clone is skipped on no-op shifts,
/// and that the insert_text_uc / delete_text_uc position-refresh loops
/// are still gated behind rope_positions_match_flow.