PieceTable: newline test from the line index, memchr rebuild; remove dead code
GetLine and LineColToByteOffset fetched each line's last byte (a piece lookup, an allocation and a cache write) to see whether it was a newline; the line index already says so: every line but the last ends at one. LineColToByteOffset runs on every insert. RebuildLineIndex scans with memchr instead of byte by byte. Removed, having no callers: OptimizedSearch (built only into its own test), UndoNodePool.h, SwapManager::RecordSplit/RecordJoin (replay still reads SPLIT/JOIN), PieceTable::Reserve/Capacity and its capacity constructor, Buffer's write-only render column, Editor's mode, no-kill, dirtyex and repeatable flags and the UCount getter, and RetryPolicy::Conservative. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
This commit is contained in:
@@ -347,7 +347,6 @@ Buffer::Buffer(const Buffer &other)
|
|||||||
{
|
{
|
||||||
curx_ = other.curx_;
|
curx_ = other.curx_;
|
||||||
cury_ = other.cury_;
|
cury_ = other.cury_;
|
||||||
rx_ = other.rx_;
|
|
||||||
nrows_ = other.nrows_;
|
nrows_ = other.nrows_;
|
||||||
rowoffs_ = other.rowoffs_;
|
rowoffs_ = other.rowoffs_;
|
||||||
coloffs_ = other.coloffs_;
|
coloffs_ = other.coloffs_;
|
||||||
@@ -401,7 +400,6 @@ Buffer::operator=(const Buffer &other)
|
|||||||
return *this;
|
return *this;
|
||||||
curx_ = other.curx_;
|
curx_ = other.curx_;
|
||||||
cury_ = other.cury_;
|
cury_ = other.cury_;
|
||||||
rx_ = other.rx_;
|
|
||||||
nrows_ = other.nrows_;
|
nrows_ = other.nrows_;
|
||||||
rowoffs_ = other.rowoffs_;
|
rowoffs_ = other.rowoffs_;
|
||||||
coloffs_ = other.coloffs_;
|
coloffs_ = other.coloffs_;
|
||||||
@@ -450,7 +448,6 @@ Buffer::operator=(const Buffer &other)
|
|||||||
Buffer::Buffer(Buffer &&other) noexcept
|
Buffer::Buffer(Buffer &&other) noexcept
|
||||||
: curx_(other.curx_),
|
: curx_(other.curx_),
|
||||||
cury_(other.cury_),
|
cury_(other.cury_),
|
||||||
rx_(other.rx_),
|
|
||||||
nrows_(other.nrows_),
|
nrows_(other.nrows_),
|
||||||
rowoffs_(other.rowoffs_),
|
rowoffs_(other.rowoffs_),
|
||||||
coloffs_(other.coloffs_),
|
coloffs_(other.coloffs_),
|
||||||
@@ -502,7 +499,6 @@ Buffer::operator=(Buffer &&other) noexcept
|
|||||||
|
|
||||||
curx_ = other.curx_;
|
curx_ = other.curx_;
|
||||||
cury_ = other.cury_;
|
cury_ = other.cury_;
|
||||||
rx_ = other.rx_;
|
|
||||||
nrows_ = other.nrows_;
|
nrows_ = other.nrows_;
|
||||||
rowoffs_ = other.rowoffs_;
|
rowoffs_ = other.rowoffs_;
|
||||||
coloffs_ = other.coloffs_;
|
coloffs_ = other.coloffs_;
|
||||||
@@ -593,7 +589,7 @@ Buffer::OpenFromFile(const std::string &path, std::string &err)
|
|||||||
dirty_ = false;
|
dirty_ = false;
|
||||||
|
|
||||||
// Reset cursor/viewport state
|
// Reset cursor/viewport state
|
||||||
curx_ = cury_ = rx_ = 0;
|
curx_ = cury_ = 0;
|
||||||
rowoffs_ = coloffs_ = 0;
|
rowoffs_ = coloffs_ = 0;
|
||||||
mark_set_ = false;
|
mark_set_ = false;
|
||||||
mark_curx_ = mark_cury_ = 0;
|
mark_curx_ = mark_cury_ = 0;
|
||||||
@@ -687,7 +683,7 @@ Buffer::OpenFromFile(const std::string &path, std::string &err)
|
|||||||
MarkContentChanged();
|
MarkContentChanged();
|
||||||
|
|
||||||
// Reset cursor/viewport state
|
// Reset cursor/viewport state
|
||||||
curx_ = cury_ = rx_ = 0;
|
curx_ = cury_ = 0;
|
||||||
rowoffs_ = coloffs_ = 0;
|
rowoffs_ = coloffs_ = 0;
|
||||||
mark_set_ = false;
|
mark_set_ = false;
|
||||||
mark_curx_ = mark_cury_ = 0;
|
mark_curx_ = mark_cury_ = 0;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* or scratch document and manages:
|
* or scratch document and manages:
|
||||||
*
|
*
|
||||||
* - Content storage: Uses PieceTable for efficient text operations
|
* - Content storage: Uses PieceTable for efficient text operations
|
||||||
* - Cursor state: Current position (curx_, cury_), rendered column (rx_)
|
* - Cursor state: Current position (curx_, cury_)
|
||||||
* - Viewport: Scroll offsets (rowoffs_, coloffs_) for display
|
* - Viewport: Scroll offsets (rowoffs_, coloffs_) for display
|
||||||
* - File backing: Optional association with a file on disk
|
* - File backing: Optional association with a file on disk
|
||||||
* - Undo/Redo: Integrated UndoSystem for operation history
|
* - Undo/Redo: Integrated UndoSystem for operation history
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
*
|
*
|
||||||
* 1. Cursor coordinates:
|
* 1. Cursor coordinates:
|
||||||
* - (curx_, cury_): Logical character position in the document
|
* - (curx_, cury_): Logical character position in the document
|
||||||
* - rx_: Rendered column accounting for tab expansion
|
|
||||||
*
|
*
|
||||||
* 2. File backing:
|
* 2. File backing:
|
||||||
* - Buffers can be file-backed (associated with a path) or scratch (unnamed)
|
* - Buffers can be file-backed (associated with a path) or scratch (unnamed)
|
||||||
@@ -119,12 +118,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] std::size_t Rx() const
|
|
||||||
{
|
|
||||||
return rx_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] std::size_t Nrows() const
|
[[nodiscard]] std::size_t Nrows() const
|
||||||
{
|
{
|
||||||
return content_LineCount_();
|
return content_LineCount_();
|
||||||
@@ -394,12 +387,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SetRenderX(const std::size_t rx)
|
|
||||||
{
|
|
||||||
rx_ = rx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetOffsets(const std::size_t row, const std::size_t col)
|
void SetOffsets(const std::size_t row, const std::size_t col)
|
||||||
{
|
{
|
||||||
rowoffs_ = row;
|
rowoffs_ = row;
|
||||||
@@ -740,7 +727,6 @@ private:
|
|||||||
|
|
||||||
// State mirroring original C struct (without undo_tree)
|
// State mirroring original C struct (without undo_tree)
|
||||||
std::size_t curx_ = 0, cury_ = 0; // cursor position in characters
|
std::size_t curx_ = 0, cury_ = 0; // cursor position in characters
|
||||||
std::size_t rx_ = 0; // render x (tabs expanded)
|
|
||||||
std::size_t nrows_ = 0; // number of rows
|
std::size_t nrows_ = 0; // number of rows
|
||||||
std::size_t rowoffs_ = 0, coloffs_ = 0; // viewport offsets
|
std::size_t rowoffs_ = 0, coloffs_ = 0; // viewport offsets
|
||||||
mutable std::vector<Line> rows_; // materialized cache of rows (without trailing newlines)
|
mutable std::vector<Line> rows_; // materialized cache of rows (without trailing newlines)
|
||||||
|
|||||||
@@ -389,7 +389,6 @@ if (BUILD_TESTS)
|
|||||||
tests/test_swap_cleanup2.cc
|
tests/test_swap_cleanup2.cc
|
||||||
tests/test_swap_git_editor.cc
|
tests/test_swap_git_editor.cc
|
||||||
tests/test_piece_table.cc
|
tests/test_piece_table.cc
|
||||||
tests/test_search.cc
|
|
||||||
tests/test_search_replace_flow.cc
|
tests/test_search_replace_flow.cc
|
||||||
tests/test_reflow_paragraph.cc
|
tests/test_reflow_paragraph.cc
|
||||||
tests/test_reflow_indented_bullets.cc
|
tests/test_reflow_indented_bullets.cc
|
||||||
@@ -418,7 +417,6 @@ if (BUILD_TESTS)
|
|||||||
SearchHighlight.cc
|
SearchHighlight.cc
|
||||||
KKeymap.cc
|
KKeymap.cc
|
||||||
SwapRecorder.h
|
SwapRecorder.h
|
||||||
OptimizedSearch.cc
|
|
||||||
UndoNode.cc
|
UndoNode.cc
|
||||||
UndoTree.cc
|
UndoTree.cc
|
||||||
UndoSystem.cc
|
UndoSystem.cc
|
||||||
|
|||||||
@@ -265,7 +265,6 @@ ensure_cursor_visible(const Editor &ed, Buffer &buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf.SetOffsets(rowoffs, coloffs);
|
buf.SetOffsets(rowoffs, coloffs);
|
||||||
buf.SetRenderX(rx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3051,7 +3050,6 @@ cmd_show_help(CommandContext &ctx)
|
|||||||
b.SetDirty(false);
|
b.SetDirty(false);
|
||||||
b.SetCursor(0, 0);
|
b.SetCursor(0, 0);
|
||||||
b.SetOffsets(0, 0);
|
b.SetOffsets(0, 0);
|
||||||
b.SetRenderX(0);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (help_index != static_cast<std::size_t>(-1)) {
|
if (help_index != static_cast<std::size_t>(-1)) {
|
||||||
|
|||||||
@@ -728,15 +728,11 @@ void
|
|||||||
Editor::Reset()
|
Editor::Reset()
|
||||||
{
|
{
|
||||||
rows_ = cols_ = 0;
|
rows_ = cols_ = 0;
|
||||||
mode_ = 0;
|
|
||||||
kill_ = 0;
|
kill_ = 0;
|
||||||
no_kill_ = 0;
|
|
||||||
dirtyex_ = 0;
|
|
||||||
msg_.clear();
|
msg_.clear();
|
||||||
msgtm_ = 0;
|
msgtm_ = 0;
|
||||||
uarg_ = 0;
|
uarg_ = 0;
|
||||||
ucount_ = 0;
|
ucount_ = 0;
|
||||||
repeatable_ = false;
|
|
||||||
quit_requested_ = false;
|
quit_requested_ = false;
|
||||||
quit_confirm_pending_ = false;
|
quit_confirm_pending_ = false;
|
||||||
// Reset close-confirm/save state
|
// Reset close-confirm/save state
|
||||||
|
|||||||
@@ -79,19 +79,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Mode and flags (mirroring legacy fields)
|
|
||||||
void SetMode(int m)
|
|
||||||
{
|
|
||||||
mode_ = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] int Mode() const
|
|
||||||
{
|
|
||||||
return mode_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetKillChain(bool on)
|
void SetKillChain(bool on)
|
||||||
{
|
{
|
||||||
kill_ = on ? 1 : 0;
|
kill_ = on ? 1 : 0;
|
||||||
@@ -104,18 +91,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SetNoKill(bool on)
|
|
||||||
{
|
|
||||||
no_kill_ = on ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] bool NoKill() const
|
|
||||||
{
|
|
||||||
return no_kill_ != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Kill ring API
|
// Kill ring API
|
||||||
void KillRingClear()
|
void KillRingClear()
|
||||||
{
|
{
|
||||||
@@ -164,18 +139,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SetDirtyEx(int d)
|
|
||||||
{
|
|
||||||
dirtyex_ = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] int DirtyEx() const
|
|
||||||
{
|
|
||||||
return dirtyex_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetUniversalArg(int uarg, int ucount)
|
void SetUniversalArg(int uarg, int ucount)
|
||||||
{
|
{
|
||||||
uarg_ = uarg;
|
uarg_ = uarg;
|
||||||
@@ -189,12 +152,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] int UCount() const
|
|
||||||
{
|
|
||||||
return ucount_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// --- Universal argument control (C-u) ---
|
// --- Universal argument control (C-u) ---
|
||||||
// Begin or extend a universal argument (like ke's uarg_start)
|
// Begin or extend a universal argument (like ke's uarg_start)
|
||||||
void UArgStart();
|
void UArgStart();
|
||||||
@@ -209,19 +166,6 @@ public:
|
|||||||
// If no universal argument active, returns 1.
|
// If no universal argument active, returns 1.
|
||||||
int UArgGet();
|
int UArgGet();
|
||||||
|
|
||||||
// Repeatable command flag: input layer can mark the next command as repeatable
|
|
||||||
void SetRepeatable(bool on)
|
|
||||||
{
|
|
||||||
repeatable_ = on;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] bool Repeatable() const
|
|
||||||
{
|
|
||||||
return repeatable_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Status message storage. Rendering is renderer-dependent; the editor
|
// Status message storage. Rendering is renderer-dependent; the editor
|
||||||
// merely stores the current message and its timestamp.
|
// merely stores the current message and its timestamp.
|
||||||
void SetStatus(const std::string &message);
|
void SetStatus(const std::string &message);
|
||||||
@@ -666,14 +610,10 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::size_t rows_ = 0, cols_ = 0;
|
std::size_t rows_ = 0, cols_ = 0;
|
||||||
int mode_ = 0;
|
|
||||||
int kill_ = 0; // KILL CHAIN
|
int kill_ = 0; // KILL CHAIN
|
||||||
int no_kill_ = 0; // don't kill in delete_row
|
|
||||||
int dirtyex_ = 0;
|
|
||||||
std::string msg_;
|
std::string msg_;
|
||||||
std::time_t msgtm_ = 0;
|
std::time_t msgtm_ = 0;
|
||||||
int uarg_ = 0, ucount_ = 0; // C-u support
|
int uarg_ = 0, ucount_ = 0; // C-u support
|
||||||
bool repeatable_ = false; // whether the next command is repeatable
|
|
||||||
|
|
||||||
std::vector<Buffer> buffers_;
|
std::vector<Buffer> buffers_;
|
||||||
std::vector<Buffer> *shared_buffers_ = nullptr; // if set, use this instead of buffers_
|
std::vector<Buffer> *shared_buffers_ = nullptr; // if set, use this instead of buffers_
|
||||||
|
|||||||
@@ -52,11 +52,6 @@ struct RetryPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Conservative policy for non-critical operations: fewer attempts, slower retries
|
|
||||||
static RetryPolicy Conservative()
|
|
||||||
{
|
|
||||||
return RetryPolicy{2, std::chrono::milliseconds(200), 2.5, std::chrono::milliseconds(10000)};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Retry a function with exponential backoff for transient errors
|
// Retry a function with exponential backoff for transient errors
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
#include "OptimizedSearch.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
OptimizedSearch::build_bad_char(const std::string &pattern)
|
|
||||||
{
|
|
||||||
if (pattern == last_pat_)
|
|
||||||
return;
|
|
||||||
last_pat_ = pattern;
|
|
||||||
std::fill(bad_char_.begin(), bad_char_.end(), -1);
|
|
||||||
for (std::size_t i = 0; i < pattern.size(); ++i) {
|
|
||||||
bad_char_[static_cast<unsigned char>(pattern[i])] = static_cast<int>(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::size_t
|
|
||||||
OptimizedSearch::find_first(const std::string &text, const std::string &pattern, std::size_t start)
|
|
||||||
{
|
|
||||||
const std::size_t n = text.size();
|
|
||||||
const std::size_t m = pattern.size();
|
|
||||||
if (m == 0)
|
|
||||||
return start <= n ? start : std::string::npos;
|
|
||||||
if (m > n || start >= n)
|
|
||||||
return std::string::npos;
|
|
||||||
build_bad_char(pattern);
|
|
||||||
std::size_t s = start;
|
|
||||||
while (s <= n - m) {
|
|
||||||
std::size_t j = m;
|
|
||||||
while (j > 0 && pattern[j - 1] == text[s + j - 1]) {
|
|
||||||
--j;
|
|
||||||
}
|
|
||||||
if (j == 0) {
|
|
||||||
return s; // match found
|
|
||||||
}
|
|
||||||
unsigned char badc = static_cast<unsigned char>(text[s + j - 1]);
|
|
||||||
int bcidx = bad_char_[badc];
|
|
||||||
std::size_t shift = (j - 1 > static_cast<std::size_t>(bcidx))
|
|
||||||
? (j - 1 - static_cast<std::size_t>(bcidx))
|
|
||||||
: 1;
|
|
||||||
s += shift;
|
|
||||||
}
|
|
||||||
return std::string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::size_t>
|
|
||||||
OptimizedSearch::find_all(const std::string &text, const std::string &pattern, std::size_t start)
|
|
||||||
{
|
|
||||||
std::vector<std::size_t> res;
|
|
||||||
const std::size_t n = text.size();
|
|
||||||
const std::size_t m = pattern.size();
|
|
||||||
if (m == 0)
|
|
||||||
return res;
|
|
||||||
if (m > n || start >= n)
|
|
||||||
return res;
|
|
||||||
build_bad_char(pattern);
|
|
||||||
std::size_t s = start;
|
|
||||||
while (s <= n - m) {
|
|
||||||
std::size_t j = m;
|
|
||||||
while (j > 0 && pattern[j - 1] == text[s + j - 1]) {
|
|
||||||
--j;
|
|
||||||
}
|
|
||||||
if (j == 0) {
|
|
||||||
res.push_back(s);
|
|
||||||
s += m; // non-overlapping
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
unsigned char badc = static_cast<unsigned char>(text[s + j - 1]);
|
|
||||||
int bcidx = bad_char_[badc];
|
|
||||||
std::size_t shift = (j - 1 > static_cast<std::size_t>(bcidx))
|
|
||||||
? (j - 1 - static_cast<std::size_t>(bcidx))
|
|
||||||
: 1;
|
|
||||||
s += shift;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// OptimizedSearch.h - Boyer–Moore (bad character) based substring search
|
|
||||||
#pragma once
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class OptimizedSearch {
|
|
||||||
public:
|
|
||||||
OptimizedSearch() = default;
|
|
||||||
|
|
||||||
// Find first occurrence at or after start. Returns npos if not found.
|
|
||||||
std::size_t find_first(const std::string &text, const std::string &pattern, std::size_t start = 0);
|
|
||||||
|
|
||||||
// Find all non-overlapping matches at or after start. Returns starting indices.
|
|
||||||
std::vector<std::size_t> find_all(const std::string &text, const std::string &pattern, std::size_t start = 0);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::array<int, 256> bad_char_{};
|
|
||||||
std::string last_pat_;
|
|
||||||
|
|
||||||
void build_bad_char(const std::string &pattern);
|
|
||||||
};
|
|
||||||
+12
-36
@@ -10,13 +10,6 @@
|
|||||||
PieceTable::PieceTable() = default;
|
PieceTable::PieceTable() = default;
|
||||||
|
|
||||||
|
|
||||||
PieceTable::PieceTable(const std::size_t initialCapacity)
|
|
||||||
{
|
|
||||||
add_.reserve(initialCapacity);
|
|
||||||
materialized_.reserve(initialCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PieceTable::PieceTable(const std::size_t initialCapacity,
|
PieceTable::PieceTable(const std::size_t initialCapacity,
|
||||||
const std::size_t piece_limit,
|
const std::size_t piece_limit,
|
||||||
const std::size_t small_piece_threshold,
|
const std::size_t small_piece_threshold,
|
||||||
@@ -109,14 +102,6 @@ PieceTable::operator=(PieceTable &&other) noexcept
|
|||||||
PieceTable::~PieceTable() = default;
|
PieceTable::~PieceTable() = default;
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
PieceTable::Reserve(const std::size_t newCapacity)
|
|
||||||
{
|
|
||||||
add_.reserve(newCapacity);
|
|
||||||
materialized_.reserve(newCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Setter to allow tuning consolidation heuristics
|
// Setter to allow tuning consolidation heuristics
|
||||||
void
|
void
|
||||||
PieceTable::SetConsolidationParams(const std::size_t piece_limit,
|
PieceTable::SetConsolidationParams(const std::size_t piece_limit,
|
||||||
@@ -417,12 +402,10 @@ PieceTable::RebuildLineIndex() const
|
|||||||
const std::string &src = pc.src == Source::Original ? original_ : add_;
|
const std::string &src = pc.src == Source::Original ? original_ : add_;
|
||||||
const char *base = src.data() + static_cast<std::ptrdiff_t>(pc.start);
|
const char *base = src.data() + static_cast<std::ptrdiff_t>(pc.start);
|
||||||
|
|
||||||
for (std::size_t j = 0; j < pc.len; ++j) {
|
const char *end = base + pc.len;
|
||||||
if (base[j] == '\n') {
|
// The next line starts after each newline.
|
||||||
// next line starts after the newline
|
for (const char *p = base; (p = static_cast<const char *>(std::memchr(p, '\n', end - p))) != nullptr; ++p)
|
||||||
line_index_.push_back(pos + j + 1);
|
line_index_.push_back(pos + static_cast<std::size_t>(p - base) + 1);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += pc.len;
|
pos += pc.len;
|
||||||
}
|
}
|
||||||
@@ -800,14 +783,10 @@ PieceTable::GetLine(std::size_t line_num) const
|
|||||||
auto [start, end] = GetLineRange(line_num);
|
auto [start, end] = GetLineRange(line_num);
|
||||||
if (end < start)
|
if (end < start)
|
||||||
return std::string();
|
return std::string();
|
||||||
// Trim trailing '\n'
|
// Every line but the last ends at a newline (each newline starts an
|
||||||
if (end > start) {
|
// index entry); leave it out.
|
||||||
// To check last char, we can get it via GetRange of len 1 at end-1 without materializing whole
|
if (end > start && line_num + 1 < line_index_.size())
|
||||||
std::string last = GetRange(end - 1, 1);
|
end -= 1;
|
||||||
if (!last.empty() && last[0] == '\n') {
|
|
||||||
end -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return GetRange(start, end - start);
|
return GetRange(start, end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,13 +816,10 @@ PieceTable::LineColToByteOffset(std::size_t row, std::size_t col) const
|
|||||||
return total_size_;
|
return total_size_;
|
||||||
std::size_t start = line_index_[row];
|
std::size_t start = line_index_[row];
|
||||||
std::size_t end = (row + 1 < line_index_.size()) ? line_index_[row + 1] : total_size_;
|
std::size_t end = (row + 1 < line_index_.size()) ? line_index_[row + 1] : total_size_;
|
||||||
// Clamp col to line length excluding trailing newline
|
// Clamp col to the line's length without its newline (every line but
|
||||||
if (end > start) {
|
// the last has one).
|
||||||
std::string last = GetRange(end - 1, 1);
|
if (end > start && row + 1 < line_index_.size())
|
||||||
if (!last.empty() && last[0] == '\n') {
|
end -= 1;
|
||||||
end -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::size_t target = start + std::min(col, end - start);
|
std::size_t target = start + std::min(col, end - start);
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,8 +52,6 @@ class PieceTable {
|
|||||||
public:
|
public:
|
||||||
PieceTable();
|
PieceTable();
|
||||||
|
|
||||||
explicit PieceTable(std::size_t initialCapacity);
|
|
||||||
|
|
||||||
// Advanced constructor allowing configuration of consolidation heuristics
|
// Advanced constructor allowing configuration of consolidation heuristics
|
||||||
PieceTable(std::size_t initialCapacity,
|
PieceTable(std::size_t initialCapacity,
|
||||||
std::size_t piece_limit,
|
std::size_t piece_limit,
|
||||||
@@ -70,9 +68,6 @@ public:
|
|||||||
|
|
||||||
~PieceTable();
|
~PieceTable();
|
||||||
|
|
||||||
// Public API mirrors GapBuffer
|
|
||||||
void Reserve(std::size_t newCapacity);
|
|
||||||
|
|
||||||
void AppendChar(char c);
|
void AppendChar(char c);
|
||||||
|
|
||||||
void Append(const char *s, std::size_t len);
|
void Append(const char *s, std::size_t len);
|
||||||
@@ -109,13 +104,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] std::size_t Capacity() const
|
|
||||||
{
|
|
||||||
// Capacity for piece table isn't directly meaningful; report materialized capacity
|
|
||||||
return materialized_.capacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ===== New buffer-wide API (Phase 1) =====
|
// ===== New buffer-wide API (Phase 1) =====
|
||||||
// Byte-based editing operations
|
// Byte-based editing operations
|
||||||
void Insert(std::size_t byte_offset, const char *text, std::size_t len);
|
void Insert(std::size_t byte_offset, const char *text, std::size_t len);
|
||||||
|
|||||||
@@ -1202,47 +1202,6 @@ SwapManager::RecordDelete(Buffer &buf, int row, int col, std::size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
SwapManager::RecordSplit(Buffer &buf, int row, int col)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lg(mtx_);
|
|
||||||
auto it = journals_.find(&buf);
|
|
||||||
if (it == journals_.end() || it->second.suspended)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Pending p;
|
|
||||||
p.buf = &buf;
|
|
||||||
p.type = SwapRecType::SPLIT;
|
|
||||||
// payload v1: [encver u8=1][row u32][col u32]
|
|
||||||
p.payload.push_back(1);
|
|
||||||
put_le32(p.payload, static_cast<std::uint32_t>(std::max(0, row)));
|
|
||||||
put_le32(p.payload, static_cast<std::uint32_t>(std::max(0, col)));
|
|
||||||
enqueue(std::move(p));
|
|
||||||
maybe_request_checkpoint(buf, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
SwapManager::RecordJoin(Buffer &buf, int row)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lg(mtx_);
|
|
||||||
auto it = journals_.find(&buf);
|
|
||||||
if (it == journals_.end() || it->second.suspended)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Pending p;
|
|
||||||
p.buf = &buf;
|
|
||||||
p.type = SwapRecType::JOIN;
|
|
||||||
// payload v1: [encver u8=1][row u32]
|
|
||||||
p.payload.push_back(1);
|
|
||||||
put_le32(p.payload, static_cast<std::uint32_t>(std::max(0, row)));
|
|
||||||
enqueue(std::move(p));
|
|
||||||
maybe_request_checkpoint(buf, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
SwapManager::maybe_request_checkpoint(Buffer &buf, const std::size_t approx_edit_bytes)
|
SwapManager::maybe_request_checkpoint(Buffer &buf, const std::size_t approx_edit_bytes)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace kte {
|
|||||||
enum class SwapRecType : std::uint8_t {
|
enum class SwapRecType : std::uint8_t {
|
||||||
INS = 1,
|
INS = 1,
|
||||||
DEL = 2,
|
DEL = 2,
|
||||||
|
// SPLIT and JOIN are no longer written; replay still reads them.
|
||||||
SPLIT = 3,
|
SPLIT = 3,
|
||||||
JOIN = 4,
|
JOIN = 4,
|
||||||
META = 0xF0,
|
META = 0xF0,
|
||||||
@@ -213,10 +214,6 @@ private:
|
|||||||
|
|
||||||
void RecordDelete(Buffer &buf, int row, int col, std::size_t len);
|
void RecordDelete(Buffer &buf, int row, int col, std::size_t len);
|
||||||
|
|
||||||
void RecordSplit(Buffer &buf, int row, int col);
|
|
||||||
|
|
||||||
void RecordJoin(Buffer &buf, int row);
|
|
||||||
|
|
||||||
void RecordCheckpoint(Buffer &buf, bool urgent_flush);
|
void RecordCheckpoint(Buffer &buf, bool urgent_flush);
|
||||||
|
|
||||||
void maybe_request_checkpoint(Buffer &buf, std::size_t approx_edit_bytes);
|
void maybe_request_checkpoint(Buffer &buf, std::size_t approx_edit_bytes);
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stack>
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
#include "UndoNode.h"
|
|
||||||
|
|
||||||
// Pool allocator for UndoNode to eliminate frequent malloc/free.
|
|
||||||
// Uses fixed-size blocks to keep node addresses stable.
|
|
||||||
class UndoNodePool {
|
|
||||||
public:
|
|
||||||
explicit UndoNodePool(std::size_t block_size = 64)
|
|
||||||
: block_size_(block_size) {}
|
|
||||||
|
|
||||||
|
|
||||||
UndoNode *acquire()
|
|
||||||
{
|
|
||||||
if (available_.empty())
|
|
||||||
allocate_block();
|
|
||||||
auto *node = available_.top();
|
|
||||||
available_.pop();
|
|
||||||
// Node comes zeroed; ensure links are reset
|
|
||||||
node->text.clear();
|
|
||||||
node->parent = nullptr;
|
|
||||||
node->child = nullptr;
|
|
||||||
node->next = nullptr;
|
|
||||||
node->row = node->col = 0;
|
|
||||||
node->type = UndoType{};
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void release(UndoNode *node)
|
|
||||||
{
|
|
||||||
if (!node)
|
|
||||||
return;
|
|
||||||
// Clear heavy fields to free memory held by strings
|
|
||||||
node->text.clear();
|
|
||||||
node->parent = nullptr;
|
|
||||||
node->child = nullptr;
|
|
||||||
node->next = nullptr;
|
|
||||||
node->row = node->col = 0;
|
|
||||||
node->type = UndoType{};
|
|
||||||
available_.push(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void allocate_block()
|
|
||||||
{
|
|
||||||
// allocate a new block; keep ownership so memory stays valid
|
|
||||||
std::unique_ptr<UndoNode[]> block(new UndoNode[block_size_]);
|
|
||||||
UndoNode *base = block.get();
|
|
||||||
blocks_.push_back(std::move(block));
|
|
||||||
for (std::size_t i = 0; i < block_size_; ++i) {
|
|
||||||
// ensure the node is reset; rely on default constructor/zero init
|
|
||||||
available_.push(&base[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::size_t block_size_;
|
|
||||||
std::vector<std::unique_ptr<UndoNode[]> > blocks_;
|
|
||||||
std::stack<UndoNode *> available_;
|
|
||||||
};
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include "OptimizedSearch.h"
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
static std::vector<std::size_t>
|
|
||||||
ref_find_all(const std::string &text, const std::string &pat)
|
|
||||||
{
|
|
||||||
std::vector<std::size_t> res;
|
|
||||||
if (pat.empty())
|
|
||||||
return res;
|
|
||||||
std::size_t from = 0;
|
|
||||||
while (true) {
|
|
||||||
auto p = text.find(pat, from);
|
|
||||||
if (p == std::string::npos)
|
|
||||||
break;
|
|
||||||
res.push_back(p);
|
|
||||||
from = p + pat.size();
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST(OptimizedSearch_basic_cases)
|
|
||||||
{
|
|
||||||
OptimizedSearch os;
|
|
||||||
struct Case {
|
|
||||||
std::string text;
|
|
||||||
std::string pat;
|
|
||||||
} cases[] = {
|
|
||||||
{"", ""},
|
|
||||||
{"", "a"},
|
|
||||||
{"a", ""},
|
|
||||||
{"a", "a"},
|
|
||||||
{"aaaaa", "aa"},
|
|
||||||
{"hello world", "world"},
|
|
||||||
{"abcabcabc", "abc"},
|
|
||||||
{"the quick brown fox", "fox"},
|
|
||||||
};
|
|
||||||
for (auto &c: cases) {
|
|
||||||
auto got = os.find_all(c.text, c.pat, 0);
|
|
||||||
auto ref = ref_find_all(c.text, c.pat);
|
|
||||||
ASSERT_EQ(got, ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user