use std::borrow::Borrow;
use std::ops::Deref;
use std::str;
use crate::crlf;
#[derive(Clone, Default)]
#[repr(C)]
pub(crate) struct NodeText(inner::NodeSmallString);
impl NodeText {
#[inline(always)]
pub fn new() -> Self {
NodeText(inner::NodeSmallString::new())
}
pub fn from_str(string: &str) -> Self {
NodeText(inner::NodeSmallString::from_str(string))
}
pub fn insert_str(&mut self, byte_idx: usize, string: &str) {
self.0.insert_str(byte_idx, string);
}
pub fn insert_str_split(&mut self, byte_idx: usize, string: &str) -> Self {
debug_assert!(self.is_char_boundary(byte_idx));
let tot_len = self.len() + string.len();
let mid_idx = tot_len / 2;
let a = byte_idx;
let b = byte_idx + string.len();
let split_idx = {
let mut buf = [0u8; 8];
let start = mid_idx - 4.min(mid_idx);
let end = (mid_idx + 4).min(tot_len);
for i in start..end {
buf[i - start] = if i < a {
self.as_bytes()[i]
} else if i < b {
string.as_bytes()[i - a]
} else {
self.as_bytes()[i - string.len()]
};
}
crlf::nearest_internal_break(mid_idx - start, &buf[..(end - start)]) + start
};
let mut right = NodeText::new();
if split_idx <= a {
right.push_str(&self[split_idx..a]);
right.push_str(string);
right.push_str(&self[a..]);
self.truncate(split_idx);
} else if split_idx <= b {
right.push_str(&string[(split_idx - a)..]);
right.push_str(&self[a..]);
self.truncate(a);
self.push_str(&string[..(split_idx - a)]);
} else {
right.push_str(&self[(split_idx - string.len())..]);
self.truncate(split_idx - string.len());
self.insert_str(a, string);
}
self.0.inline_if_possible();
right
}
pub fn push_str(&mut self, string: &str) {
let len = self.len();
self.0.insert_str(len, string);
}
pub fn push_str_split(&mut self, string: &str) -> Self {
let len = self.len();
self.insert_str_split(len, string)
}
pub fn truncate(&mut self, byte_idx: usize) {
self.0.truncate(byte_idx);
self.0.inline_if_possible();
}
pub fn truncate_front(&mut self, byte_idx: usize) {
self.0.remove_range(0, byte_idx);
self.0.inline_if_possible();
}
pub fn remove_range(&mut self, byte_start: usize, byte_end: usize) {
self.0.remove_range(byte_start, byte_end);
self.0.inline_if_possible();
}
pub fn split_off(&mut self, byte_idx: usize) -> Self {
let other = NodeText(self.0.split_off(byte_idx));
self.0.inline_if_possible();
other
}
}
impl std::cmp::PartialEq for NodeText {
fn eq(&self, other: &Self) -> bool {
let (s1, s2): (&str, &str) = (self, other);
s1 == s2
}
}
impl<'a> PartialEq<NodeText> for &'a str {
fn eq(&self, other: &NodeText) -> bool {
*self == (other as &str)
}
}
impl<'a> PartialEq<&'a str> for NodeText {
fn eq(&self, other: &&'a str) -> bool {
(self as &str) == *other
}
}
impl std::fmt::Display for NodeText {
fn fmt(&self, fm: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
NodeText::deref(self).fmt(fm)
}
}
impl std::fmt::Debug for NodeText {
fn fmt(&self, fm: &mut std::fmt::Formatter) -> std::fmt::Result {
NodeText::deref(self).fmt(fm)
}
}
impl<'a> From<&'a str> for NodeText {
fn from(s: &str) -> Self {
Self::from_str(s)
}
}
impl Deref for NodeText {
type Target = str;
fn deref(&self) -> &str {
self.0.as_str()
}
}
impl AsRef<str> for NodeText {
fn as_ref(&self) -> &str {
self.0.as_str()
}
}
impl Borrow<str> for NodeText {
fn borrow(&self) -> &str {
self.0.as_str()
}
}
pub(crate) fn fix_segment_seam(l: &mut NodeText, r: &mut NodeText) {
if crlf::seam_is_break(l.as_bytes(), r.as_bytes()) {
return;
}
let tot_len = l.len() + r.len();
let new_split_pos = {
let l_split = crlf::prev_break(l.len(), l.as_bytes());
let r_split = l.len() + crlf::next_break(0, r.as_bytes());
if l_split != 0 && (r_split == tot_len || l.len() > r.len()) {
l_split
} else {
r_split
}
};
if new_split_pos < l.len() {
r.insert_str(0, &l[new_split_pos..]);
l.truncate(new_split_pos);
} else {
let pos = new_split_pos - l.len();
l.push_str(&r[..pos]);
r.truncate_front(pos);
}
}
mod inner {
use crate::tree::MAX_BYTES;
use smallvec::{Array, SmallVec};
use std::str;
#[derive(Copy, Clone)]
struct BackingArray([u8; MAX_BYTES]);
unsafe impl Array for BackingArray {
type Item = u8;
fn size() -> usize {
MAX_BYTES
}
}
#[derive(Clone, Default)]
#[repr(C)]
pub struct NodeSmallString {
buffer: SmallVec<BackingArray>,
}
impl NodeSmallString {
#[inline(always)]
pub fn new() -> Self {
NodeSmallString {
buffer: SmallVec::new(),
}
}
#[inline(always)]
pub fn with_capacity(capacity: usize) -> Self {
NodeSmallString {
buffer: SmallVec::with_capacity(capacity),
}
}
#[inline(always)]
pub fn from_str(string: &str) -> Self {
let mut nodetext = NodeSmallString::with_capacity(string.len());
nodetext.insert_str(0, string);
nodetext
}
#[inline(always)]
pub fn len(&self) -> usize {
self.buffer.len()
}
#[inline(always)]
pub fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
}
#[inline(always)]
pub fn insert_str(&mut self, byte_idx: usize, string: &str) {
assert!(self.as_str().is_char_boundary(byte_idx));
self.buffer.insert_from_slice(byte_idx, string.as_bytes());
}
#[inline(always)]
pub fn remove_range(&mut self, start_byte_idx: usize, end_byte_idx: usize) {
assert!(start_byte_idx <= end_byte_idx);
debug_assert!(end_byte_idx <= self.len());
assert!(self.as_str().is_char_boundary(start_byte_idx));
assert!(self.as_str().is_char_boundary(end_byte_idx));
let len = self.len();
let amt = end_byte_idx - start_byte_idx;
self.buffer.copy_within(end_byte_idx..len, start_byte_idx);
self.buffer.truncate(len - amt);
}
#[inline(always)]
pub fn truncate(&mut self, byte_idx: usize) {
debug_assert!(byte_idx <= self.len());
assert!(self.as_str().is_char_boundary(byte_idx));
self.buffer.truncate(byte_idx);
}
#[inline(always)]
pub fn split_off(&mut self, byte_idx: usize) -> Self {
debug_assert!(byte_idx <= self.len());
assert!(self.as_str().is_char_boundary(byte_idx));
let len = self.len();
let mut other = NodeSmallString::with_capacity(len - byte_idx);
other.buffer.extend_from_slice(&self.buffer[byte_idx..]);
self.buffer.truncate(byte_idx);
other
}
#[inline(always)]
pub fn inline_if_possible(&mut self) {
if self.buffer.spilled() && (self.buffer.len() <= self.buffer.inline_size()) {
self.buffer.shrink_to_fit();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn small_string_basics() {
let s = NodeSmallString::from_str("Hello!");
assert_eq!("Hello!", s.as_str());
assert_eq!(6, s.len());
}
#[test]
fn insert_str_01() {
let mut s = NodeSmallString::from_str("Hello!");
s.insert_str(3, "oz");
assert_eq!("Helozlo!", s.as_str());
}
#[test]
#[should_panic]
fn insert_str_02() {
let mut s = NodeSmallString::from_str("Hello!");
s.insert_str(7, "oz");
}
#[test]
#[should_panic]
fn insert_str_03() {
let mut s = NodeSmallString::from_str("こんにちは");
s.insert_str(4, "oz");
}
#[test]
fn remove_range_01() {
let mut s = NodeSmallString::from_str("Hello!");
s.remove_range(2, 4);
assert_eq!("Heo!", s.as_str());
}
#[test]
#[should_panic]
fn remove_range_02() {
let mut s = NodeSmallString::from_str("Hello!");
s.remove_range(4, 2);
}
#[test]
#[should_panic]
fn remove_range_03() {
let mut s = NodeSmallString::from_str("Hello!");
s.remove_range(2, 7);
}
#[test]
#[should_panic]
fn remove_range_04() {
let mut s = NodeSmallString::from_str("こんにちは");
s.remove_range(2, 4);
}
#[test]
fn truncate_01() {
let mut s = NodeSmallString::from_str("Hello!");
s.truncate(4);
assert_eq!("Hell", s.as_str());
}
#[test]
#[should_panic]
fn truncate_02() {
let mut s = NodeSmallString::from_str("Hello!");
s.truncate(7);
}
#[test]
#[should_panic]
fn truncate_03() {
let mut s = NodeSmallString::from_str("こんにちは");
s.truncate(4);
}
#[test]
fn split_off_01() {
let mut s1 = NodeSmallString::from_str("Hello!");
let s2 = s1.split_off(4);
assert_eq!("Hell", s1.as_str());
assert_eq!("o!", s2.as_str());
}
#[test]
#[should_panic]
fn split_off_02() {
let mut s1 = NodeSmallString::from_str("Hello!");
s1.split_off(7);
}
#[test]
#[should_panic]
fn split_off_03() {
let mut s1 = NodeSmallString::from_str("こんにちは");
s1.split_off(4);
}
}
}