string32 0.2.0

A string that is indexed by u32 instead of usize
Documentation
use std::borrow::{Borrow, BorrowMut, Cow};
use std::cmp;
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
use std::mem;
use std::ops;
use std::string;

use mediumvec::Vec32;
use usize_cast::IntoUsize;

use super::{Str32, TryFromStrError, TryFromStringError};

/// A string that is indexed by `u32` instead of `usize`.
///
/// On 64-bit platforms, `String32` only requires 16 bytes to store the pointer, length, and capacity. [`String`] by comparison requires 24 bytes, plus padding.
#[derive(Clone, Debug, Default, Eq)]
#[repr(transparent)]
pub struct String32(Vec32<u8>);

impl String32 {
    /// Create an empty `String32`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// let s = String32::new();
    /// ```
    #[must_use]
    pub fn new() -> Self {
        Self(Vec32::new())
    }

    /// Create an empty `String32` with enough capacity to hold `cap` bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// let mut s = String32::with_capacity(1);
    /// let cap = s.capacity();
    /// s.push('\n');
    /// assert_eq!(cap, s.capacity());
    /// ```
    #[must_use]
    pub fn with_capacity(cap: u32) -> Self {
        Self(Vec32::with_capacity(cap))
    }

    /// Return the capacity of this `String32` in bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// let mut s = String32::new();
    /// assert_eq!(0, s.capacity());
    /// s.push('\n');
    /// assert!(s.capacity() > 0);
    /// ```
    #[must_use]
    pub fn capacity(&self) -> u32 {
        self.0.capacity()
    }

    /// A helper to call arbitrary [`String`] methods on a `String32.`
    ///
    /// # Panics
    ///
    /// Panics if the resulting string would require more than [`u32::MAX`] bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// let mut s = String32::new();
    /// s.as_string(|s| s.push_str("test"));
    /// assert_eq!(s, "test");
    /// ```
    pub fn as_string<F, T>(&mut self, f: F) -> T
    where
        F: FnOnce(&mut String) -> T,
    {
        let mut s = mem::take(self).into();
        let ret = f(&mut s);
        *self = s.try_into().unwrap();
        ret
    }

    /// Push a `char` to the end of this `String32`.
    ///
    /// # Panics
    ///
    /// Panics if the resulting string would require more than [`u32::MAX`] bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// let mut s = String32::new();
    /// s.push('\n');
    /// assert_eq!(s, "\n");
    /// ```
    pub fn push(&mut self, ch: char) {
        self.as_string(|s| s.push(ch));
    }

    /// Append a string slice to the end of this `String32`.
    ///
    /// # Panics
    ///
    /// Panics if the resulting string would require more than [`u32::MAX`] bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// let mut s = String32::new();
    /// s.push_str("test");
    /// assert_eq!(s, "test");
    /// ```
    pub fn push_str<S>(&mut self, s: S)
    where
        S: AsRef<str>,
    {
        self.as_string(|st| st.push_str(s.as_ref()));
    }

    /// Pop a `char` from the end of this `String32`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("\n").unwrap();
    /// assert_eq!(s.pop(), Some('\n'));
    /// assert_eq!(s.pop(), None);
    /// ```
    pub fn pop(&mut self) -> Option<char> {
        self.as_string(String::pop)
    }

    /// Return the `char` at a given byte index.
    ///
    /// # Panics
    ///
    /// Panics if `idx` is not a UTF-8 code point boundary.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("abbc").unwrap();
    /// assert_eq!(s.remove(1), 'b');
    /// assert_eq!(s, "abc");
    /// ```
    pub fn remove(&mut self, idx: u32) -> char {
        self.as_string(|s| s.remove(idx.into_usize()))
    }

    /// Insert a `char` at a given byte index.
    ///
    /// # Panics
    ///
    /// Panics if `idx` is not a UTF-8 code point boundary, or if the resulting string would require more than [`u32::MAX`] bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("ac").unwrap();
    /// s.insert(1, 'b');
    /// assert_eq!(s, "abc");
    /// ```
    pub fn insert(&mut self, idx: u32, ch: char) {
        self.as_string(|s| s.insert(idx.into_usize(), ch));
    }

    /// Insert a string slice at the given byte index.
    ///
    /// # Panics
    ///
    /// Panics if `idx` is not a UTF-8 code point boundary, or if the resulting string would require more than [`u32::MAX`] bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("ad").unwrap();
    /// s.insert_str(1, "bc");
    /// assert_eq!(s, "abcd");
    /// ```
    pub fn insert_str<S>(&mut self, idx: u32, s: S)
    where
        S: AsRef<str>,
    {
        self.as_string(|st| st.insert_str(idx.into_usize(), s.as_ref()));
    }

    /// Reserve space for additional bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("abc").unwrap();
    /// s.reserve(10);
    /// println!("{}", s.capacity());
    /// assert!(s.capacity() >= 13);
    /// ```
    pub fn reserve(&mut self, additional: u32) {
        self.0.reserve(additional)
    }

    /// Reserve space for an exact number of bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// let mut s = String32::with_capacity(5);
    /// s.reserve_exact(10);
    /// assert!(s.capacity() >= 10);
    /// ```
    pub fn reserve_exact(&mut self, additional: u32) {
        self.0.reserve_exact(additional)
    }

    /// Shrink the capacity of this `String32` to match its length.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// let mut s = String32::with_capacity(10);
    /// s.shrink_to_fit();
    /// assert_eq!(0, s.capacity());
    /// ```
    pub fn shrink_to_fit(&mut self) {
        self.as_string(String::shrink_to_fit);
    }

    /// Shortens this `String32` to the specified length.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("abcde").unwrap();
    /// s.truncate(3);
    /// assert_eq!(s, "abc");
    /// ```
    pub fn truncate(&mut self, new_len: u32) {
        self.as_string(|s| s.truncate(new_len.into_usize()));
    }

    /// Truncates the `String32` into an empty string.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("abc").unwrap();
    /// s.clear();
    /// assert!(s.is_empty());
    /// ```
    pub fn clear(&mut self) {
        self.0.clear()
    }

    /// Converts a `String32` into a vector of bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let s = String32::try_from("123").unwrap();
    /// let v = s.into_bytes();
    /// assert_eq!(v, b"123");
    /// ```
    #[must_use]
    pub fn into_bytes(self) -> Vec<u8> {
        self.0.into_vec()
    }

    /// Converts a `String32` into a [`Box<str>`].
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let s = String32::try_from("123").unwrap();
    /// let b = s.into_boxed_str();
    /// ```
    #[must_use]
    pub fn into_boxed_str(self) -> Box<str> {
        String::from(self).into_boxed_str()
    }

    /// Splits the `String32` into two at the given byte index.
    ///
    /// # Panics
    ///
    /// Panics if the index is out-of-bounds or is not a UTF-8 code point boundary.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s1 = String32::try_from("123abc").unwrap();
    /// let s2 = s1.split_off(3);
    /// assert_eq!("123", s1);
    /// assert_eq!("abc", s2);
    /// ```
    #[must_use = "if you plan to discard the second half, consider using `String32::truncate` instead"]
    pub fn split_off(&mut self, at: u32) -> Self {
        self.as_string(|s| s.split_off(at.into_usize()).try_into().unwrap())
    }

    /// Create a new `String32` from a raw pointer and corresponding length/capacity.
    ///
    /// # Safety
    ///
    /// See [`String::from_raw_parts`].
    pub unsafe fn from_raw_parts(buf: *mut u8, len: u32, cap: u32) -> Self {
        Self(Vec32::from_vec(Vec::from_raw_parts(
            buf,
            len.into_usize(),
            cap.into_usize(),
        )))
    }

    /// Decodes a UTF-8 encoded vector of bytes into a `String32`.
    ///
    /// # Errors
    ///
    /// Returns `Err` if the slice is not valid UTF-8.
    ///
    /// # Panics
    ///
    /// Panics if the provided [`Vec<u8>`] holds more than [`u32::MAX`] bytes.
    pub fn from_utf8(v: Vec<u8>) -> Result<Self, string::FromUtf8Error> {
        String::from_utf8(v).map(|s| s.try_into().unwrap())
    }

    /// Decodes a UTF-16 encoded slice into a `String32`.
    ///
    /// # Errors
    ///
    /// Returns `Err` if the slice is not valid UTF-16.
    ///
    /// # Panics
    ///
    /// Panics if the resulting UTF-8 representation would require more than [`u32::MAX`] bytes.
    pub fn from_utf16(v: &[u16]) -> Result<Self, string::FromUtf16Error> {
        String::from_utf16(v).map(|s| s.try_into().unwrap())
    }

    /// Lossily decodes a UTF-16 encoded slice into a `String32`.
    ///
    /// # Panics
    ///
    /// Panics if the resulting UTF-8 representation would require more than [`u32::MAX`] bytes.
    #[must_use]
    pub fn from_utf16_lossy(v: &[u16]) -> Self {
        String::from_utf16_lossy(v).try_into().unwrap()
    }
}

impl ops::Add<&str> for String32 {
    type Output = Self;

    #[must_use]
    fn add(mut self, rhs: &str) -> Self {
        self.push_str(rhs);
        self
    }
}

impl ops::Add<&Str32> for String32 {
    type Output = Self;

    #[must_use]
    fn add(mut self, rhs: &Str32) -> Self {
        self.push_str(rhs);
        self
    }
}

impl ops::AddAssign<&str> for String32 {
    fn add_assign(&mut self, rhs: &str) {
        self.push_str(rhs);
    }
}

impl ops::AddAssign<&Str32> for String32 {
    fn add_assign(&mut self, rhs: &Str32) {
        self.push_str(rhs);
    }
}

impl AsMut<Str32> for String32 {
    fn as_mut(&mut self) -> &mut Str32 {
        self
    }
}

impl AsRef<Str32> for String32 {
    fn as_ref(&self) -> &Str32 {
        &*self
    }
}

impl AsRef<[u8]> for String32 {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

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

impl Borrow<Str32> for String32 {
    fn borrow(&self) -> &Str32 {
        &*self
    }
}

impl BorrowMut<Str32> for String32 {
    fn borrow_mut(&mut self) -> &mut Str32 {
        &mut *self
    }
}

impl ops::Deref for String32 {
    type Target = Str32;

    fn deref(&self) -> &Str32 {
        let ptr = self.0.as_ref() as *const [u8] as *const str as *const Str32;
        unsafe {
            // safety: relies on `&Str32` and `&str` having the same layout
            &*ptr
        }
    }
}

impl ops::DerefMut for String32 {
    fn deref_mut(&mut self) -> &mut Str32 {
        let ptr = self.0.as_mut() as *mut [u8] as *mut str as *mut Str32;
        unsafe {
            // safety: relies on `&mut Str32` and `&mut str` having the same layout
            &mut *ptr
        }
    }
}

impl fmt::Display for String32 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        <Str32 as fmt::Display>::fmt(self, f)
    }
}

impl From<&Str32> for String32 {
    fn from(s: &Str32) -> Self {
        s.to_owned()
    }
}

#[allow(clippy::fallible_impl_from)]
impl From<String32> for String {
    fn from(s: String32) -> Self {
        if cfg!(debug_assertions) {
            Self::from_utf8(s.0.into_vec()).unwrap()
        } else {
            unsafe {
                // safety: we never store a non-utf8 Vec32<u8> in a String32
                Self::from_utf8_unchecked(s.0.into_vec())
            }
        }
    }
}

impl From<String32> for Vec<u8> {
    fn from(s: String32) -> Self {
        s.0.into_vec()
    }
}

impl FromIterator<char> for String32 {
    fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
        String::from_iter(iter).try_into().unwrap()
    }
}

impl<'a> FromIterator<&'a char> for String32 {
    fn from_iter<I: IntoIterator<Item = &'a char>>(iter: I) -> Self {
        String::from_iter(iter).try_into().unwrap()
    }
}

impl<'a> FromIterator<&'a str> for String32 {
    fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
        String::from_iter(iter).try_into().unwrap()
    }
}

impl<'a> FromIterator<&'a Str32> for String32 {
    fn from_iter<I: IntoIterator<Item = &'a Str32>>(iter: I) -> Self {
        String::from_iter(iter.into_iter().map(Str32::as_str))
            .try_into()
            .unwrap()
    }
}

impl FromIterator<Box<str>> for String32 {
    fn from_iter<I: IntoIterator<Item = Box<str>>>(iter: I) -> Self {
        String::from_iter(iter).try_into().unwrap()
    }
}

impl FromIterator<Box<Str32>> for String32 {
    fn from_iter<I: IntoIterator<Item = Box<Str32>>>(iter: I) -> Self {
        String::from_iter(iter.into_iter().map(Str32::into_boxed_str))
            .try_into()
            .unwrap()
    }
}

impl FromIterator<String> for String32 {
    fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
        String::from_iter(iter).try_into().unwrap()
    }
}

impl FromIterator<Self> for String32 {
    fn from_iter<I: IntoIterator<Item = Self>>(iter: I) -> Self {
        String::from_iter(iter.into_iter().map(String::from))
            .try_into()
            .unwrap()
    }
}

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

impl Ord for String32 {
    fn cmp(&self, rhs: &Self) -> cmp::Ordering {
        <Str32 as Ord>::cmp(self, rhs)
    }
}

impl PartialEq for String32 {
    fn eq(&self, rhs: &Self) -> bool {
        <Str32 as PartialEq>::eq(self, rhs)
    }
}

impl PartialOrd for String32 {
    fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
        <Str32 as PartialOrd>::partial_cmp(self, rhs)
    }
}

macro_rules! impl_cmp {
    ($lhs:ty, $rhs: ty) => {
        impl<'a, 'b> PartialEq<$rhs> for $lhs {
            fn eq(&self, rhs: &$rhs) -> bool {
                <[u8] as PartialEq>::eq(self.as_bytes(), rhs.as_bytes())
            }
        }

        impl<'a, 'b> PartialEq<$lhs> for $rhs {
            fn eq(&self, rhs: &$lhs) -> bool {
                <[u8] as PartialEq>::eq(self.as_bytes(), rhs.as_bytes())
            }
        }

        impl<'a, 'b> PartialOrd<$rhs> for $lhs {
            fn partial_cmp(&self, rhs: &$rhs) -> Option<cmp::Ordering> {
                <[u8] as PartialOrd>::partial_cmp(self.as_bytes(), rhs.as_bytes())
            }
        }

        impl<'a, 'b> PartialOrd<$lhs> for $rhs {
            fn partial_cmp(&self, rhs: &$lhs) -> Option<cmp::Ordering> {
                <[u8] as PartialOrd>::partial_cmp(self.as_bytes(), rhs.as_bytes())
            }
        }
    };
}

impl_cmp!(String32, Str32);
impl_cmp!(String32, &'a Str32);
impl_cmp!(String32, String);
impl_cmp!(String32, str);
impl_cmp!(String32, &'a str);
impl_cmp!(String32, Cow<'a, Str32>);
impl_cmp!(String32, Cow<'a, str>);
impl_cmp!(String32, Box<str>);
impl_cmp!(String32, Box<Str32>);
impl_cmp!(Str32, &'a Str32);
impl_cmp!(Str32, String);
impl_cmp!(Str32, str);
impl_cmp!(Str32, &'a str);
impl_cmp!(Str32, Cow<'a, Str32>);
impl_cmp!(Str32, Cow<'a, str>);
impl_cmp!(Str32, Box<str>);
impl_cmp!(Str32, Box<Str32>);
impl_cmp!(&'a Str32, String);
impl_cmp!(&'a Str32, str);
impl_cmp!(&'a Str32, Cow<'b, Str32>);
impl_cmp!(&'a Str32, Cow<'b, str>);
impl_cmp!(&'a Str32, Box<str>);
impl_cmp!(&'a Str32, Box<Str32>);

impl TryFrom<String> for String32 {
    type Error = TryFromStringError<String>;

    fn try_from(s: String) -> Result<Self, Self::Error> {
        match u32::try_from(s.len()) {
            Ok(_) => Ok(Self(Vec32::from_vec(s.into_bytes()))),
            Err(_) => Err(TryFromStringError(s)),
        }
    }
}

impl TryFrom<Box<str>> for String32 {
    type Error = TryFromStringError<Box<str>>;

    fn try_from(s: Box<str>) -> Result<Self, Self::Error> {
        match u32::try_from(s.len()) {
            Ok(_) => Ok(Self(Vec32::from_vec(String::from(s).into_bytes()))),
            Err(_) => Err(TryFromStringError(s)),
        }
    }
}

impl TryFrom<&str> for String32 {
    type Error = TryFromStrError;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        match u32::try_from(s.len()) {
            Ok(_) => Ok(Self(Vec32::from_vec(s.to_owned().into_bytes()))),
            Err(_) => Err(TryFromStrError(())),
        }
    }
}