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)",
|
"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 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)",
|
"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)",
|
"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-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)",
|
"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 = "1.*"
|
||||||
serde_derive = "1.*"
|
serde_derive = "1.*"
|
||||||
docopt = "0.8"
|
docopt = "0.8"
|
||||||
|
smallvec = "0.6"
|
||||||
termion = "1.5"
|
termion = "1.5"
|
|
@ -3,6 +3,7 @@ extern crate ropey;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
extern crate smallvec;
|
||||||
extern crate termion;
|
extern crate termion;
|
||||||
extern crate unicode_segmentation;
|
extern crate unicode_segmentation;
|
||||||
extern crate unicode_width;
|
extern crate unicode_width;
|
||||||
|
|
|
@ -15,6 +15,7 @@ use utils::digit_count;
|
||||||
|
|
||||||
pub mod formatter;
|
pub mod formatter;
|
||||||
mod screen;
|
mod screen;
|
||||||
|
mod smallstring;
|
||||||
|
|
||||||
use self::screen::{Color, Screen, Style};
|
use self::screen::{Color, Screen, Style};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::cell::RefCell;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
use super::smallstring::SmallString;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use termion;
|
use termion;
|
||||||
|
@ -12,7 +13,7 @@ use termion::raw::{IntoRawMode, RawTerminal};
|
||||||
|
|
||||||
pub(crate) struct Screen {
|
pub(crate) struct Screen {
|
||||||
out: RefCell<AlternateScreen<RawTerminal<io::Stdout>>>,
|
out: RefCell<AlternateScreen<RawTerminal<io::Stdout>>>,
|
||||||
buf: RefCell<Vec<Option<(Style, String)>>>,
|
buf: RefCell<Vec<Option<(Style, SmallString)>>>,
|
||||||
w: usize,
|
w: usize,
|
||||||
h: usize,
|
h: usize,
|
||||||
}
|
}
|
||||||
|
@ -20,7 +21,7 @@ pub(crate) struct Screen {
|
||||||
impl Screen {
|
impl Screen {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
let (w, h) = termion::terminal_size().unwrap();
|
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)
|
.take(w as usize * h as usize)
|
||||||
.collect();
|
.collect();
|
||||||
Screen {
|
Screen {
|
||||||
|
@ -40,7 +41,7 @@ impl Screen {
|
||||||
text.push_str(" ");
|
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) {
|
pub(crate) fn resize(&mut self, w: usize, h: usize) {
|
||||||
self.w = w;
|
self.w = w;
|
||||||
self.h = h;
|
self.h = h;
|
||||||
self.buf.borrow_mut().resize(
|
self.buf
|
||||||
w * h,
|
.borrow_mut()
|
||||||
Some((Style(Color::Black, Color::Black), " ".to_string())),
|
.resize(w * h, Some((Style(Color::Black, Color::Black), " ".into())));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn present(&self) {
|
pub(crate) fn present(&self) {
|
||||||
|
@ -99,7 +99,7 @@ impl Screen {
|
||||||
for g in UnicodeSegmentation::graphemes(text, true) {
|
for g in UnicodeSegmentation::graphemes(text, true) {
|
||||||
let width = UnicodeWidthStr::width(g);
|
let width = UnicodeWidthStr::width(g);
|
||||||
if width > 0 {
|
if width > 0 {
|
||||||
buf[y * self.w + x] = Some((style, g.to_string()));
|
buf[y * self.w + x] = Some((style, g.into()));
|
||||||
x += 1;
|
x += 1;
|
||||||
for _ in 0..(width - 1) {
|
for _ in 0..(width - 1) {
|
||||||
buf[y * self.w + x] = None;
|
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