msime-engine 0.1.1

Input method engine for Chinese and other languages: quanpin, shuangpin, nine-key, wubi, zhuyin, Cantonese, Japanese, Korean and more, ranked over a word lattice with typo correction and learning from what the user picks. The engine of 水杉输入法 (Metasequoia IME).
Documentation
//! 候选行字段用的写时复制字符串。
//!
//! 九键一次刷新要把同一批词库行在行缓存、候选页、去重表之间来回复制,采样里约三到四成的时间花在 `String` 的分配、拷贝和释放上。`SharedString` 内部是 `Arc<String>`:克隆只加一次引用计数;读取经 `Deref` 得到 `String`,原来的 `as_str`、`&str` 传参和比较照常可用;改写经 `DerefMut` 走 `Arc::make_mut`,只有别处也持有同一份时才先复制,所以每处 `push_str`、`clear` 的结果与 `String` 完全相同。`Debug` 与 `String` 输出一致,golden 记录的字段格式不变。

use std::borrow::Borrow;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::sync::Arc;

/// 没有缓冲区的空串记为 `None`,`Default`、`mem::take` 与 `String::default()` 一样不分配;行结构里大多数可选字段(`corrected_from`、未知的 `canonical_pinyin`)都是空的。
#[derive(Clone, Default)]
pub struct SharedString(Option<Arc<String>>);

static EMPTY: String = String::new();

impl SharedString {
    pub fn new() -> Self {
        Self::default()
    }

    /// 取出内容:唯一持有时不复制。
    pub fn into_string(self) -> String {
        match self.0 {
            None => String::new(),
            Some(shared) => Arc::try_unwrap(shared).unwrap_or_else(|shared| (*shared).clone()),
        }
    }

    /// 遮住 `String::clear`:唯一持有时原地清空、留着缓冲区给接下来的 `push_str`;空串或与别处共享时直接放手,不像经 `DerefMut` 那样先建一个空串或把整串复制一份再清掉。
    pub fn clear(&mut self) {
        match self.0.as_mut().and_then(Arc::get_mut) {
            Some(owned) => owned.clear(),
            None => self.0 = None,
        }
    }

    fn shares_storage_with(&self, other: &Self) -> bool {
        match (&self.0, &other.0) {
            (Some(left), Some(right)) => Arc::ptr_eq(left, right),
            _ => false,
        }
    }
}

impl Deref for SharedString {
    type Target = String;

    fn deref(&self) -> &String {
        self.0.as_deref().unwrap_or(&EMPTY)
    }
}

impl DerefMut for SharedString {
    fn deref_mut(&mut self) -> &mut String {
        Arc::make_mut(self.0.get_or_insert_with(Default::default))
    }
}

impl fmt::Debug for SharedString {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(self.as_str(), formatter)
    }
}

impl fmt::Display for SharedString {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self.as_str(), formatter)
    }
}

impl PartialEq for SharedString {
    fn eq(&self, other: &Self) -> bool {
        self.shares_storage_with(other) || self.as_str() == other.as_str()
    }
}

impl Eq for SharedString {}

impl PartialOrd for SharedString {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for SharedString {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.as_str().cmp(other.as_str())
    }
}

impl Hash for SharedString {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_str().hash(state);
    }
}

impl PartialEq<str> for SharedString {
    fn eq(&self, other: &str) -> bool {
        self.as_str() == other
    }
}

impl PartialEq<&str> for SharedString {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other
    }
}

impl PartialEq<String> for SharedString {
    fn eq(&self, other: &String) -> bool {
        self.as_str() == other.as_str()
    }
}

impl PartialEq<SharedString> for str {
    fn eq(&self, other: &SharedString) -> bool {
        self == other.as_str()
    }
}

impl PartialEq<SharedString> for &str {
    fn eq(&self, other: &SharedString) -> bool {
        *self == other.as_str()
    }
}

impl PartialEq<SharedString> for String {
    fn eq(&self, other: &SharedString) -> bool {
        self.as_str() == other.as_str()
    }
}

impl Borrow<str> for SharedString {
    fn borrow(&self) -> &str {
        self.as_str()
    }
}

impl AsRef<str> for SharedString {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl From<String> for SharedString {
    /// 带容量的空串照样包进去:调用方可能正把复用的缓冲区交回来。
    fn from(value: String) -> Self {
        if value.capacity() == 0 {
            Self(None)
        } else {
            Self(Some(Arc::new(value)))
        }
    }
}

impl From<&str> for SharedString {
    fn from(value: &str) -> Self {
        Self::from(value.to_owned())
    }
}

impl From<&String> for SharedString {
    fn from(value: &String) -> Self {
        Self::from(value.clone())
    }
}

impl From<SharedString> for String {
    fn from(value: SharedString) -> Self {
        value.into_string()
    }
}

impl serde::Serialize for SharedString {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_str(self.as_str())
    }
}

impl<'de> serde::Deserialize<'de> for SharedString {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        String::deserialize(deserializer).map(Self::from)
    }
}

impl rusqlite::ToSql for SharedString {
    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
        self.as_str().to_sql()
    }
}

impl PartialEq<&String> for SharedString {
    fn eq(&self, other: &&String) -> bool {
        self.as_str() == other.as_str()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn clones_share_until_one_side_writes() {
        let mut first = SharedString::from("ni'hao");
        let second = first.clone();
        assert!(first.shares_storage_with(&second));
        first.push_str("'ma");
        assert_eq!(first, "ni'hao'ma");
        assert_eq!(second, "ni'hao");
        assert!(!first.shares_storage_with(&second));
    }

    #[test]
    fn clearing_keeps_owned_storage_and_releases_shared_storage() {
        let mut owned = SharedString::from("ni'hao");
        let buffer = owned.as_ptr();
        owned.clear();
        assert_eq!(owned, "");
        assert_eq!(owned.as_ptr(), buffer);
        let mut shared = SharedString::from("ni'hao");
        let other = shared.clone();
        shared.clear();
        assert!(shared.0.is_none());
        assert_eq!(other, "ni'hao");
        let mut empty = SharedString::default();
        empty.clear();
        assert!(empty.0.is_none());
    }

    #[test]
    fn empty_strings_hold_no_storage_until_written() {
        let mut empty = SharedString::default();
        assert!(empty.0.is_none());
        assert_eq!(empty, "");
        assert!(SharedString::from(String::new()).0.is_none());
        assert!(SharedString::from("").0.is_none());
        assert!(SharedString::from(String::with_capacity(8)).0.is_some());
        let mut written = SharedString::from("ni");
        let taken = std::mem::take(&mut written);
        assert!(written.0.is_none());
        assert_eq!(taken, "ni");
        empty.push_str("hao");
        assert_eq!(empty.into_string(), "hao");
        assert_eq!(SharedString::default().into_string(), "");
    }

    #[test]
    fn debug_and_comparisons_match_string() {
        let shared = SharedString::from("你好\"");
        let plain = String::from("你好\"");
        assert_eq!(format!("{shared:?}"), format!("{plain:?}"));
        assert_eq!(shared, plain);
        assert_eq!(plain, shared);
        assert_eq!(shared, "你好\"");
        assert_eq!(shared.as_str(), "你好\"");
        assert_eq!(shared.into_string(), plain);
    }

    #[test]
    fn hashing_matches_the_borrowed_str_for_map_lookups() {
        let mut set = std::collections::HashSet::new();
        set.insert(SharedString::from("ni"));
        assert!(set.contains("ni"));
    }
}