Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ resolver = "2"
edition = "2024"
license = "MIT"
repository = "https://github.com/microsoft/edit"
rust-version = "1.88"
rust-version = "1.93"

# We use `opt-level = "s"` as it significantly reduces binary size.
# We could then use the `#[optimize(speed)]` attribute for spot optimizations.
Expand Down
25 changes: 12 additions & 13 deletions crates/edit/benches/lib.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#![feature(allocator_api)]

use std::hint::black_box;
use std::io::Cursor;
use std::{mem, vec};

use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use edit::helpers::*;
use edit::simd::MemsetSafe;
use edit::{buffer, glob, hash, json, oklab, simd, unicode};
use stdext::arena::{self, Arena, scratch_arena};
use stdext::arena::{self, scratch_arena};
use stdext::collections::BVec;

struct EditingTracePatch<'a>(usize, usize, &'a str);

struct EditingTraceTransaction<'a> {
patches: Vec<EditingTracePatch<'a>, &'a Arena>,
patches: BVec<'a, EditingTracePatch<'a>>,
}

struct EditingTraceData<'a> {
start_content: &'a str,
end_content: &'a str,
txns: Vec<EditingTraceTransaction<'a>, &'a Arena>,
txns: BVec<'a, EditingTraceTransaction<'a>>,
}

fn bench_buffer(c: &mut Criterion) {
Expand All @@ -39,24 +37,25 @@ fn bench_buffer(c: &mut Criterion) {
let mut res = EditingTraceData {
start_content: root.get_str("startContent").unwrap(),
end_content: root.get_str("endContent").unwrap(),
txns: Vec::with_capacity_in(txns.len(), &scratch),
txns: BVec::empty(),
};
res.txns.reserve(&*scratch, txns.len());

for txn in txns {
let txn = txn.as_object().unwrap();
let patches = txn.get_array("patches").unwrap();
let mut txn =
EditingTraceTransaction { patches: Vec::with_capacity_in(patches.len(), &scratch) };
let mut txn = EditingTraceTransaction { patches: BVec::empty() };
txn.patches.reserve(&*scratch, patches.len());

for patch in patches {
let patch = patch.as_array().unwrap();
let offset = patch[0].as_number().unwrap() as usize;
let del_len = patch[1].as_number().unwrap() as usize;
let ins_str = patch[2].as_str().unwrap();
txn.patches.push(EditingTracePatch(offset, del_len, ins_str));
txn.patches.push(&*scratch, EditingTracePatch(offset, del_len, ins_str));
}

res.txns.push(txn);
res.txns.push(&*scratch, txn);
}

res
Expand Down Expand Up @@ -226,7 +225,7 @@ fn bench_simd_memchr2(c: &mut Criterion) {
}
}

fn bench_simd_memset<T: MemsetSafe + Copy + Default>(c: &mut Criterion) {
fn bench_simd_memset<T: Copy + Default>(c: &mut Criterion) {
let mut group = c.benchmark_group("simd");
let name = format!("memset<{}>", std::any::type_name::<T>());
let size = mem::size_of::<T>();
Expand All @@ -241,7 +240,7 @@ fn bench_simd_memset<T: MemsetSafe + Copy + Default>(c: &mut Criterion) {
&bytes,
|b, &bytes| {
let slice = unsafe { buf.get_unchecked_mut(..bytes / size) };
b.iter(|| simd::memset(black_box(slice), Default::default()));
b.iter(|| stdext::simd::memset(black_box(slice), Default::default()));
},
);
}
Expand Down
16 changes: 9 additions & 7 deletions crates/edit/src/base64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

//! Base64 facilities.

use stdext::arena::ArenaString;
use stdext::arena::Arena;
use stdext::collections::BString;

const CHARSET: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

Expand All @@ -15,15 +16,15 @@ pub fn encode_len(src_len: usize) -> usize {
}

/// Encodes the given bytes as base64 and appends them to the destination string.
pub fn encode(dst: &mut ArenaString, src: &[u8]) {
pub fn encode<'a>(arena: &'a Arena, dst: &mut BString<'a>, src: &[u8]) {
unsafe {
let mut inp = src.as_ptr();
let mut remaining = src.len();
let dst = dst.as_mut_vec();

let out_len = encode_len(src.len());
// ... we can then use this fact to reserve space all at once.
dst.reserve(out_len);
dst.reserve(arena, out_len);

// SAFETY: Getting a pointer to the reserved space is only safe
// *after* calling `reserve()` as it may change the pointer.
Expand Down Expand Up @@ -79,16 +80,17 @@ pub fn encode(dst: &mut ArenaString, src: &[u8]) {

#[cfg(test)]
mod tests {
use stdext::arena::{Arena, ArenaString};
use stdext::arena::scratch_arena;
use stdext::collections::BString;

use super::encode;

#[test]
fn test_basic() {
let arena = Arena::new(4 * 1024).unwrap();
let scratch = scratch_arena(None);
let enc = |s: &[u8]| {
let mut dst = ArenaString::new_in(&arena);
encode(&mut dst, s);
let mut dst = BString::empty();
encode(&scratch, &mut dst, s);
dst
};
assert_eq!(enc(b""), "");
Expand Down
8 changes: 5 additions & 3 deletions crates/edit/src/bin/edit/draw_filepicker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use edit::input::{kbmod, vk};
use edit::tui::*;
use edit::{icu, path};
use stdext::arena::scratch_arena;
use stdext::collections::BVec;

use crate::localization::*;
use crate::state::*;
Expand Down Expand Up @@ -376,9 +377,10 @@ fn update_autocomplete_suggestions(state: &mut State) {
// The problem is finding the upper bound. Here I'm using a trick:
// By appending U+10FFFF (the highest possible Unicode code point)
// we create a needle that naturally yields an upper bound.
let mut needle_upper_bound = Vec::with_capacity_in(needle.len() + 4, &*scratch);
needle_upper_bound.extend_from_slice(needle);
needle_upper_bound.extend_from_slice(b"\xf4\x8f\xbf\xbf");
let mut needle_upper_bound = BVec::empty();
needle_upper_bound.reserve(&*scratch, needle.len() + 4);
needle_upper_bound.extend_from_slice(&*scratch, needle);
needle_upper_bound.extend_from_slice(&*scratch, b"\xf4\x8f\xbf\xbf");

if let Some(dirs_files) = &state.file_picker_entries {
'outer: for entries in &dirs_files[1..] {
Expand Down
5 changes: 3 additions & 2 deletions crates/edit/src/bin/edit/draw_statusbar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use edit::input::vk;
use edit::tui::*;
use stdext::arena::scratch_arena;
use stdext::arena_format;
use stdext::collections::BVec;

use crate::localization::*;
use crate::state::*;
Expand Down Expand Up @@ -291,14 +292,14 @@ fn encoding_picker_update_list(state: &mut State) {

let encodings = icu::get_available_encodings();
let scratch = scratch_arena(None);
let mut matches = Vec::new_in(&*scratch);
let mut matches = BVec::empty();

for enc in encodings.all {
let local_scratch = scratch_arena(Some(&scratch));
let (score, _) = score_fuzzy(&local_scratch, enc.label, needle, true);

if score > 0 {
matches.push((score, *enc));
matches.push(&*scratch, (score, *enc));
}
}

Expand Down
59 changes: 33 additions & 26 deletions crates/edit/src/bin/edit/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#![feature(allocator_api, linked_list_cursors, string_from_utf8_lossy_owned)]
#![feature(linked_list_cursors, string_from_utf8_lossy_owned)]

mod apperr;
mod documents;
Expand All @@ -13,7 +13,7 @@ mod localization;
mod state;

use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::path::Path;
use std::time::Duration;
use std::{env, process};

Expand All @@ -30,8 +30,9 @@ use edit::vt::{self, Token};
use edit::{base64, path, sys, unicode};
use localization::*;
use state::*;
use stdext::arena::{self, Arena, ArenaString, scratch_arena};
use stdext::arena::{self, Arena, scratch_arena};
use stdext::arena_format;
use stdext::collections::{BString, BVec};

#[cfg(target_pointer_width = "32")]
const SCRATCH_ARENA_CAPACITY: usize = 128 * MEBI;
Expand Down Expand Up @@ -172,23 +173,23 @@ fn run() -> apperr::Result<()> {
let scratch = scratch_arena(None);
let mut output = tui.render(&scratch);

write_terminal_title(&mut output, &mut state);
write_terminal_title(&scratch, &mut output, &mut state);

if state.osc_clipboard_sync {
write_osc_clipboard(&mut tui, &mut state, &mut output);
write_osc_clipboard(&scratch, &mut output, &mut tui, &mut state);
}

#[cfg(feature = "debug-latency")]
{
use std::fmt::Write as _;
use stdext::arena_write_fmt;

// Print the number of passes and latency in the top right corner.
let time_end = std::time::Instant::now();
let status = time_end - time_beg;

let scratch_alt = scratch_arena(Some(&scratch));
let status = arena_format!(
&scratch_alt,
&*scratch_alt,
"{}P {}B {:.3}μs",
passes,
output.len(),
Expand All @@ -204,10 +205,11 @@ fn run() -> apperr::Result<()> {
// If the `output` is already very large,
// Rust may double the size during the write below.
// Let's avoid that by reserving the needed size in advance.
output.reserve_exact(128);
output.reserve_exact(&*scratch, 128);

// To avoid moving the cursor, push and pop it onto the VT cursor stack.
_ = write!(
arena_write_fmt!(
&*scratch,
output,
"\x1b7\x1b[0;41;97m\x1b[1;{0}H{1:2$}{3}\x1b8",
tui.size().width - cols - padding + 1,
Expand All @@ -229,7 +231,7 @@ fn run() -> apperr::Result<()> {
// Returns true if the application should exit early.
fn handle_args(state: &mut State) -> apperr::Result<bool> {
let scratch = scratch_arena(None);
let mut paths: Vec<PathBuf, &Arena> = Vec::new_in(&*scratch);
let mut paths = BVec::empty();
let cwd = env::current_dir()?;
let mut dir = None;
let mut parse_args = true;
Expand Down Expand Up @@ -261,7 +263,7 @@ fn handle_args(state: &mut State) -> apperr::Result<bool> {
state.wants_file_picker = StateFilePicker::Open;
dir = Some(p);
} else {
paths.push(p);
paths.push(&*scratch, p);
}
}

Expand Down Expand Up @@ -394,7 +396,7 @@ fn draw_handle_wants_exit(_ctx: &mut Context, state: &mut State) {
}
}

fn write_terminal_title(output: &mut ArenaString, state: &mut State) {
fn write_terminal_title<'a>(arena: &'a Arena, output: &mut BString<'a>, state: &mut State) {
let (filename, dirty) = state
.documents
.active()
Expand All @@ -406,15 +408,15 @@ fn write_terminal_title(output: &mut ArenaString, state: &mut State) {
return;
}

output.push_str("\x1b]0;");
output.push_str(arena, "\x1b]0;");
if !filename.is_empty() {
if dirty {
output.push_str("● ");
output.push_str(arena, "● ");
}
output.push_str(&sanitize_control_chars(filename));
output.push_str(" - ");
output.push_str(arena, &sanitize_control_chars(filename));
output.push_str(arena, " - ");
}
output.push_str("edit\x1b\\");
output.push_str(arena, "edit\x1b\\");

state.osc_title_file_status.filename = filename.to_string();
state.osc_title_file_status.dirty = dirty;
Expand Down Expand Up @@ -449,10 +451,10 @@ fn draw_handle_clipboard_change(ctx: &mut Context, state: &mut State) {
let template = loc(LocId::LargeClipboardWarningLine2);
let size = arena_format!(ctx.arena(), "{}", MetricFormatter(data_len));

let mut label =
ArenaString::with_capacity_in(template.len() + size.len(), ctx.arena());
label.push_str(template);
label.replace_once_in_place("{size}", &size);
let mut label = BString::empty();
label.reserve(ctx.arena(), template.len() + size.len());
label.push_str(ctx.arena(), template);
label.replace_once_in_place(ctx.arena(), "{size}", &size);
label
};

Expand Down Expand Up @@ -514,7 +516,12 @@ fn draw_handle_clipboard_change(ctx: &mut Context, state: &mut State) {
}

#[cold]
fn write_osc_clipboard(tui: &mut Tui, state: &mut State, output: &mut ArenaString) {
fn write_osc_clipboard<'a>(
arena: &'a Arena,
output: &mut BString<'a>,
tui: &mut Tui,
state: &mut State,
) {
let clipboard = tui.clipboard_mut();
let data = clipboard.read();

Expand All @@ -523,10 +530,10 @@ fn write_osc_clipboard(tui: &mut Tui, state: &mut State, output: &mut ArenaStrin
// If `data` is *really* large, this may then double
// the size of the `output` from e.g. 100MB to 200MB. Not good.
// We can avoid that by reserving the needed size in advance.
output.reserve_exact(base64::encode_len(data.len()) + 16);
output.push_str("\x1b]52;c;");
base64::encode(output, data);
output.push_str("\x1b\\");
output.reserve_exact(arena, base64::encode_len(data.len()) + 16);
output.push_str(arena, "\x1b]52;c;");
base64::encode(arena, output, data);
output.push_str(arena, "\x1b\\");
}

state.osc_clipboard_sync = false;
Expand Down
Loading