Using a smallstring implementation inside the screen buffer.
This keeps things contiguous in memory in the common case.
This commit is contained in:
parent
0a309fab1a
commit
17beb9c06d
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -6,6 +6,7 @@ dependencies = [
|
|||
"ropey 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -15,4 +15,5 @@ unicode-width = "0.1"
|
|||
serde = "1.*"
|
||||
serde_derive = "1.*"
|
||||
docopt = "0.8"
|
||||
smallvec = "0.6"
|
||||
termion = "1.5"
|
|
@ -3,6 +3,7 @@ extern crate ropey;
|
|||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate smallvec;
|
||||
extern crate termion;
|
||||
extern crate unicode_segmentation;
|
||||
extern crate unicode_width;
|
||||
|
|
|
@ -15,6 +15,7 @@ use utils::digit_count;
|
|||
|
||||
pub mod formatter;
|
||||
mod screen;
|
||||
mod smallstring;
|
||||
|
||||
use self::screen::{Color, Screen, Style};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::cell::RefCell;
|
|||
use std::io;
|
||||
use std::io::Write;
|
||||
|
||||
use super::smallstring::SmallString;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use termion;
|
||||
|
@ -12,7 +13,7 @@ use termion::raw::{IntoRawMode, RawTerminal};
|
|||
|
||||
pub(crate) struct Screen {
|
||||
out: RefCell<AlternateScreen<RawTerminal<io::Stdout>>>,
|
||||
buf: RefCell<Vec<Option<(Style, String)>>>,
|
||||
buf: RefCell<Vec<Option<(Style, SmallString)>>>,
|
||||
w: usize,
|
||||
h: usize,
|
||||
}
|
||||
|
@ -20,7 +21,7 @@ pub(crate) struct Screen {
|
|||
impl Screen {
|
||||
pub(crate) fn new() -> Self {
|
||||
let (w, h) = termion::terminal_size().unwrap();
|
||||
let buf = std::iter::repeat(Some((Style(Color::Black, Color::Black), " ".to_string())))
|
||||
let buf = std::iter::repeat(Some((Style(Color::Black, Color::Black), " ".into())))
|
||||
.take(w as usize * h as usize)
|
||||
.collect();
|
||||
Screen {
|
||||
|
@ -40,7 +41,7 @@ impl Screen {
|
|||
text.push_str(" ");
|
||||
}
|
||||
_ => {
|
||||
*cell = Some((Style(Color::Black, Color::Black), " ".to_string()));
|
||||
*cell = Some((Style(Color::Black, Color::Black), " ".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,10 +50,9 @@ impl Screen {
|
|||
pub(crate) fn resize(&mut self, w: usize, h: usize) {
|
||||
self.w = w;
|
||||
self.h = h;
|
||||
self.buf.borrow_mut().resize(
|
||||
w * h,
|
||||
Some((Style(Color::Black, Color::Black), " ".to_string())),
|
||||
);
|
||||
self.buf
|
||||
.borrow_mut()
|
||||
.resize(w * h, Some((Style(Color::Black, Color::Black), " ".into())));
|
||||
}
|
||||
|
||||
pub(crate) fn present(&self) {
|
||||
|
@ -99,7 +99,7 @@ impl Screen {
|
|||
for g in UnicodeSegmentation::graphemes(text, true) {
|
||||
let width = UnicodeWidthStr::width(g);
|
||||
if width > 0 {
|
||||
buf[y * self.w + x] = Some((style, g.to_string()));
|
||||
buf[y * self.w + x] = Some((style, g.into()));
|
||||
x += 1;
|
||||
for _ in 0..(width - 1) {
|
||||
buf[y * self.w + x] = None;
|
||||
|
|
158
src/term_ui/smallstring.rs
Normal file
158
src/term_ui/smallstring.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
use std;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::ops::Deref;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct SmallString {
|
||||
buffer: SmallVec<[u8; 8]>,
|
||||
}
|
||||
|
||||
impl SmallString {
|
||||
/// Creates a new empty `SmallString`
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
SmallString {
|
||||
buffer: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new empty `SmallString` with at least `capacity` bytes
|
||||
/// of capacity.
|
||||
#[inline(always)]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
SmallString {
|
||||
buffer: SmallVec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `SmallString` with the same contents as the given `&str`.
|
||||
pub fn from_str(text: &str) -> Self {
|
||||
let mut string = SmallString::with_capacity(text.len());
|
||||
unsafe { string.insert_bytes(0, text.as_bytes()) };
|
||||
string
|
||||
}
|
||||
|
||||
/// Appends a `&str` to end the of the `SmallString`.
|
||||
pub fn push_str(&mut self, string: &str) {
|
||||
let len = self.len();
|
||||
unsafe {
|
||||
self.insert_bytes(len, string.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
/// Drops the text after byte index `idx`.
|
||||
///
|
||||
/// Panics if `idx` is not a char boundary, as that would result in an
|
||||
/// invalid utf8 string.
|
||||
pub fn truncate(&mut self, idx: usize) {
|
||||
assert!(self.is_char_boundary(idx));
|
||||
debug_assert!(idx <= self.len());
|
||||
self.buffer.truncate(idx);
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.truncate(0);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) {
|
||||
assert!(idx <= self.len());
|
||||
let len = self.len();
|
||||
let amt = bytes.len();
|
||||
self.buffer.reserve(amt);
|
||||
|
||||
ptr::copy(
|
||||
self.buffer.as_ptr().offset(idx as isize),
|
||||
self.buffer.as_mut_ptr().offset((idx + amt) as isize),
|
||||
len - idx,
|
||||
);
|
||||
ptr::copy(
|
||||
bytes.as_ptr(),
|
||||
self.buffer.as_mut_ptr().offset(idx as isize),
|
||||
amt,
|
||||
);
|
||||
self.buffer.set_len(len + amt);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn remove_bytes(&mut self, start: usize, end: usize) {
|
||||
assert!(start <= end);
|
||||
assert!(end <= self.len());
|
||||
let len = self.len();
|
||||
let amt = end - start;
|
||||
ptr::copy(
|
||||
self.buffer.as_ptr().offset(end as isize),
|
||||
self.buffer.as_mut_ptr().offset(start as isize),
|
||||
len - end,
|
||||
);
|
||||
self.buffer.set_len(len - amt);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::cmp::PartialEq for SmallString {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let (s1, s2): (&str, &str) = (self, other);
|
||||
s1 == s2
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<SmallString> for &'a str {
|
||||
fn eq(&self, other: &SmallString) -> bool {
|
||||
*self == (other as &str)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for SmallString {
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
(self as &str) == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SmallString {
|
||||
fn fmt(&self, fm: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
SmallString::deref(self).fmt(fm)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SmallString {
|
||||
fn fmt(&self, fm: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
SmallString::deref(self).fmt(fm)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for SmallString {
|
||||
fn from(s: &str) -> Self {
|
||||
Self::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SmallString {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &str {
|
||||
// SmallString's methods don't allow `buffer` to become invalid utf8,
|
||||
// so this is safe.
|
||||
unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for SmallString {
|
||||
fn as_ref(&self) -> &str {
|
||||
// SmallString's methods don't allow `buffer` to become invalid utf8,
|
||||
// so this is safe.
|
||||
unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for SmallString {
|
||||
fn borrow(&self) -> &str {
|
||||
// SmallString's methods don't allow `buffer` to become invalid utf8,
|
||||
// so this is safe.
|
||||
unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user