diff --git a/src/ascii_char.rs b/src/ascii_char.rs index 05a81b4..1bb281e 100644 --- a/src/ascii_char.rs +++ b/src/ascii_char.rs @@ -1,8 +1,6 @@ -extern crate core; - -use self::core::mem::transmute; -use self::core::cmp::Ordering; -use self::core::{fmt, char}; +use core::mem::transmute; +use core::cmp::Ordering; +use core::{fmt, char}; #[cfg(feature = "std")] use std::error::Error; #[cfg(feature = "std")] diff --git a/src/ascii_str.rs b/src/ascii_str.rs index ff2e0ed..2861e4c 100644 --- a/src/ascii_str.rs +++ b/src/ascii_str.rs @@ -1,7 +1,6 @@ -extern crate core; - -use self::core::{fmt, mem}; -use self::core::ops::{Index, IndexMut, Range, RangeTo, RangeFrom, RangeFull}; +use core::{fmt, mem, slice}; +use core::slice::{Iter, IterMut}; +use core::ops::{Index, IndexMut, Range, RangeTo, RangeFrom, RangeFull}; #[cfg(feature = "std")] use std::error::Error; #[cfg(feature = "std")] @@ -15,38 +14,50 @@ use ascii_string::AsciiString; /// /// It wraps an `[AsciiChar]` and implements many of `str`s methods and traits. /// -/// It can be created by a checked conversion from a `str` or `[u8]`, -/// or borrowed from an `AsciiString`. +/// It can be created by a checked conversion from a `str` or `[u8]`, or borrowed from an +/// `AsciiString`. #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AsciiStr { slice: [AsciiChar], } impl AsciiStr { - /// Coerces into an `AsciiStr` slice. + /// Coerces `s` into an `AsciiStr` slice. pub fn new + ?Sized>(s: &S) -> &AsciiStr { s.as_ref() } - /// Converts `&self` to a `&str` slice. + /// Converts the ascii string slice to a UTF-8 string slice. #[inline] pub fn as_str(&self) -> &str { unsafe { mem::transmute(&self.slice) } } - /// Converts `&self` into a byte slice. + /// Converts the ascii string slice to a mutable UTF-8 string slice. + #[inline] + pub fn as_mut_str(&mut self) -> &mut str { + unsafe { mem::transmute(&mut self.slice) } + } + + /// Converts the ascii string slice into a byte slice. #[inline] pub fn as_bytes(&self) -> &[u8] { unsafe { mem::transmute(&self.slice) } } - /// Returns the entire string as slice of `AsciiChar`s. + /// Converts the ascii string slice into a mutable byte slice. + #[inline] + pub fn as_mut_bytes(&mut self) -> &mut [u8] { + unsafe { mem::transmute(&mut self.slice) } + } + + /// Returns the entire ascii string slice as slice of `AsciiChar`s. #[inline] pub fn as_slice(&self) -> &[AsciiChar] { &self.slice } - /// Returns the entire string as mutable slice of `AsciiChar`s. + /// Returns the entire ascii string slice as mutable slice of `AsciiChar`s. #[inline] pub fn as_mut_slice(&mut self) -> &mut [AsciiChar] { &mut self.slice @@ -139,6 +150,72 @@ impl AsciiStr { self.len() == 0 } + /// Divide one string slice into two at an index. + /// + /// The argument, `mid`, should be a byte offset from the start of the string. The two slices + /// returned go from the start of the string slice to `mid`, and from `mid` to the end of the + /// string slice. + /// + /// To get mutable string slices instead, see the `split_at_mut()` method. + /// + /// # Panics + /// + /// Panics if `mid` is beyond the end of the string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ascii::AsciiStr; + /// let s = AsciiStr::from_ascii("Per Martin-Lof").unwrap(); + /// let (first, last) = s.split_at(3); + /// + /// assert_eq!("Per", first.as_str()); + /// assert_eq!(" Martin-Lof", last.as_str()); + /// ``` + pub fn split_at(&self, mid: usize) -> (&AsciiStr, &AsciiStr) { + assert!(mid <= self.len()); + (&self[..mid], &self[mid..]) + } + + /// Divide one mutable string slice into two at an index. + /// + /// The argument, `mid`, should be a byte offset from the start of the string. The two slices + /// returned go from the start of the string slice to `mid`, and from `mid` to the end of the + /// string slice. + /// + /// To get immutable string slices instead, see the `split_at()` method. + /// + /// # Panics + /// + /// Panics if `mid` is beyond the end of the string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ascii::{AsMutAsciiStr, AsciiStr}; + /// let mut s = String::from("Per Martin-Lof"); + /// let (first, last) = s.as_mut_ascii_str().unwrap().split_at_mut(3); + /// + /// assert_eq!("Per", first.as_str()); + /// assert_eq!(" Martin-Lof", last.as_str()); + /// ``` + pub fn split_at_mut(&mut self, mid: usize) -> (&mut AsciiStr, &mut AsciiStr) { + let len = self.len(); + assert!(mid <= len); + unsafe { + let ptr = self.as_mut_ptr(); + // This now has three mutable references pointing at the same memory. `self`, the rvalue + // ret.0, and the rvalue ret.1. `self` is never used after `let ptr = ...`, and so one + // can treat it as "dead", and therefore, you only have two real mutable slices. + (slice::from_raw_parts_mut(ptr, mid).as_mut_ascii_str_unchecked(), + slice::from_raw_parts_mut(ptr.offset(mid as isize), len - mid).as_mut_ascii_str_unchecked()) + } + } + /// Returns an ASCII string slice with leading and trailing whitespace removed. /// /// # Examples @@ -237,12 +314,24 @@ impl AsRef<[u8]> for AsciiStr { self.as_bytes() } } +impl AsMut<[u8]> for AsciiStr { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + self.as_mut_bytes() + } +} impl AsRef for AsciiStr { #[inline] fn as_ref(&self) -> &str { self.as_str() } } +impl AsMut for AsciiStr { + #[inline] + fn as_mut(&mut self) -> &mut str { + self.as_mut_str() + } +} impl AsRef<[AsciiChar]> for AsciiStr { #[inline] fn as_ref(&self) -> &[AsciiChar] { @@ -531,6 +620,25 @@ impl AsMutAsciiStr for str { } } +impl<'a> IntoIterator for &'a AsciiStr { + type Item = &'a AsciiChar; + type IntoIter = Iter<'a, AsciiChar>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.slice.iter() + } +} +impl<'a> IntoIterator for &'a mut AsciiStr { + type Item = &'a mut AsciiChar; + type IntoIter = IterMut<'a, AsciiChar>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.slice.iter_mut() + } +} + #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index 7ec55ae..105abf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,9 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "std")] +extern crate core; + mod free_functions; mod ascii_char; mod ascii_str;