#[cfg(feature = "std")]
use std::borrow::Cow;
use core::cmp;
#[cfg(feature = "std")]
use std::iter;
use core::mem;
use core::ops;
use core::ptr;
use core::slice;
use core::str;
use memchr::{memchr, memrchr};
use ascii;
#[cfg(feature = "std")]
use bstring::BString;
use search::{PrefilterState, TwoWay};
use slice_index::SliceIndex;
#[cfg(feature = "unicode")]
use unicode::{
Graphemes, GraphemeIndices,
Sentences, SentenceIndices,
Words, WordIndices, WordsWithBreaks, WordsWithBreakIndices,
whitespace_len_fwd, whitespace_len_rev,
};
use utf8::{self, Chars, CharIndices, Utf8Error};
#[allow(non_snake_case)]
pub fn B<'a, B: ?Sized + AsRef<[u8]>>(bytes: &'a B) -> &'a BStr {
BStr::new(bytes.as_ref())
}
#[derive(Hash)]
pub struct BStr {
bytes: [u8],
}
impl BStr {
pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &BStr {
BStr::from_bytes(bytes.as_ref())
}
pub fn new_mut<B: ?Sized + AsMut<[u8]>>(bytes: &mut B) -> &mut BStr {
BStr::from_bytes_mut(bytes.as_mut())
}
pub fn from_bytes(slice: &[u8]) -> &BStr {
unsafe { mem::transmute(slice) }
}
pub fn from_bytes_mut(slice: &mut [u8]) -> &mut BStr {
unsafe { mem::transmute(slice) }
}
pub unsafe fn from_raw_parts<'a>(data: *const u8, len: usize) -> &'a BStr {
BStr::new(slice::from_raw_parts(data, len))
}
pub unsafe fn from_raw_parts_mut<'a>(
data: *mut u8,
len: usize,
) -> &'a mut BStr {
BStr::new_mut(slice::from_raw_parts_mut(data, len))
}
pub fn len(&self) -> usize {
self.bytes.len()
}
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.bytes
}
#[cfg(feature = "std")]
pub fn to_bstring(&self) -> BString {
BString::from_vec(self.as_bytes().to_vec())
}
pub fn to_str(&self) -> Result<&str, Utf8Error> {
utf8::validate(self.as_bytes()).map(|_| {
unsafe {
str::from_utf8_unchecked(self.as_bytes())
}
})
}
pub unsafe fn to_str_unchecked(&self) -> &str {
str::from_utf8_unchecked(self.as_bytes())
}
#[cfg(feature = "std")]
pub fn to_str_lossy(&self) -> Cow<str> {
match utf8::validate(self.as_bytes()) {
Ok(()) => {
unsafe {
Cow::Borrowed(str::from_utf8_unchecked(self.as_bytes()))
}
}
Err(err) => {
let mut lossy = String::with_capacity(self.len());
let (valid, after) = self
.as_bytes()
.split_at(err.valid_up_to());
lossy.push_str(unsafe { str::from_utf8_unchecked(valid) });
lossy.push_str("\u{FFFD}");
if let Some(len) = err.error_len() {
B(&after[len..]).to_str_lossy_into(&mut lossy);
}
Cow::Owned(lossy)
}
}
}
#[cfg(feature = "std")]
pub fn to_str_lossy_into(&self, dest: &mut String) {
dest.reserve(self.len());
let mut bytes = self.as_bytes();
loop {
match utf8::validate(bytes) {
Ok(()) => {
dest.push_str(unsafe { str::from_utf8_unchecked(bytes) });
break;
}
Err(err) => {
let (valid, after) = bytes.split_at(err.valid_up_to());
dest.push_str(unsafe { str::from_utf8_unchecked(valid) });
dest.push_str("\u{FFFD}");
match err.error_len() {
None => break,
Some(len) => bytes = &after[len..],
}
}
}
}
}
#[cfg(feature = "std")]
pub fn repeat(&self, n: usize) -> BString {
iter::repeat(self).take(n).collect()
}
pub fn contains<B: AsRef<[u8]>>(&self, needle: B) -> bool {
self.find(needle).is_some()
}
pub fn starts_with<B: AsRef<[u8]>>(&self, prefix: B) -> bool {
let prefix = prefix.as_ref();
self.get(..prefix.len()).map_or(false, |x| x == prefix)
}
pub fn ends_with<B: AsRef<[u8]>>(&self, suffix: B) -> bool {
let suffix = suffix.as_ref();
self.len()
.checked_sub(suffix.len())
.map_or(false, |s| &self[s..] == suffix)
}
pub fn find<B: AsRef<[u8]>>(&self, needle: B) -> Option<usize> {
Finder::new(needle.as_ref()).find(self)
}
pub fn rfind<B: AsRef<[u8]>>(&self, needle: B) -> Option<usize> {
FinderReverse::new(needle.as_ref()).rfind(self)
}
pub fn find_iter<'a, B: ?Sized + AsRef<[u8]>>(
&'a self,
needle: &'a B,
) -> Find<'a> {
Find::new(self, BStr::new(needle.as_ref()))
}
pub fn rfind_iter<'a, B: ?Sized + AsRef<[u8]>>(
&'a self,
needle: &'a B,
) -> FindReverse<'a> {
FindReverse::new(self, BStr::new(needle.as_ref()))
}
pub fn find_byte(&self, byte: u8) -> Option<usize> {
memchr(byte, self.as_bytes())
}
pub fn rfind_byte(&self, byte: u8) -> Option<usize> {
memrchr(byte, self.as_bytes())
}
pub fn find_char(&self, ch: char) -> Option<usize> {
self.find(ch.encode_utf8(&mut [0; 4]))
}
pub fn rfind_char(&self, ch: char) -> Option<usize> {
self.rfind(ch.encode_utf8(&mut [0; 4]))
}
pub fn fields(&self) -> Fields {
Fields::new(self)
}
pub fn fields_with<F: FnMut(char) -> bool>(&self, f: F) -> FieldsWith<F> {
FieldsWith::new(self, f)
}
pub fn split<'a, B: ?Sized + AsRef<[u8]>>(
&'a self,
splitter: &'a B,
) -> Split<'a> {
Split::new(self, BStr::new(splitter.as_ref()))
}
pub fn rsplit<'a, B: ?Sized + AsRef<[u8]>>(
&'a self,
splitter: &'a B,
) -> SplitReverse<'a> {
SplitReverse::new(self, BStr::new(splitter.as_ref()))
}
pub fn splitn<'a, B: ?Sized + AsRef<[u8]>>(
&'a self,
limit: usize,
splitter: &'a B,
) -> SplitN<'a> {
SplitN::new(self, BStr::new(splitter.as_ref()), limit)
}
pub fn rsplitn<'a, B: ?Sized + AsRef<[u8]>>(
&'a self,
limit: usize,
splitter: &'a B,
) -> SplitNReverse<'a> {
SplitNReverse::new(self, BStr::new(splitter.as_ref()), limit)
}
#[cfg(feature = "std")]
pub fn replace<N: AsRef<[u8]>, R: AsRef<[u8]>>(
&self,
needle: N,
replacement: R,
) -> BString {
let mut dest = BString::with_capacity(self.len());
self.replace_into(needle, replacement, &mut dest);
dest
}
#[cfg(feature = "std")]
pub fn replacen<N: AsRef<[u8]>, R: AsRef<[u8]>>(
&self,
needle: N,
replacement: R,
limit: usize,
) -> BString {
let mut dest = BString::with_capacity(self.len());
self.replacen_into(needle, replacement, limit, &mut dest);
dest
}
#[cfg(feature = "std")]
pub fn replace_into<N: AsRef<[u8]>, R: AsRef<[u8]>>(
&self,
needle: N,
replacement: R,
dest: &mut BString,
) {
let (needle, replacement) = (needle.as_ref(), replacement.as_ref());
let mut last = 0;
for start in self.find_iter(needle) {
dest.push(&self[last..start]);
dest.push(replacement);
last = start + needle.len();
}
dest.push(&self[last..]);
}
#[cfg(feature = "std")]
pub fn replacen_into<N: AsRef<[u8]>, R: AsRef<[u8]>>(
&self,
needle: N,
replacement: R,
limit: usize,
dest: &mut BString,
) {
let (needle, replacement) = (needle.as_ref(), replacement.as_ref());
let mut last = 0;
for start in self.find_iter(needle).take(limit) {
dest.push(&self[last..start]);
dest.push(replacement);
last = start + needle.len();
}
dest.push(&self[last..]);
}
pub fn bytes(&self) -> Bytes {
Bytes { it: self.as_bytes().iter() }
}
pub fn chars(&self) -> Chars {
Chars::new(self)
}
pub fn char_indices(&self) -> CharIndices {
CharIndices::new(self)
}
#[cfg(feature = "unicode")]
pub fn graphemes(&self) -> Graphemes {
Graphemes::new(self)
}
#[cfg(feature = "unicode")]
pub fn grapheme_indices(&self) -> GraphemeIndices {
GraphemeIndices::new(self)
}
#[cfg(feature = "unicode")]
pub fn words(&self) -> Words {
Words::new(self)
}
#[cfg(feature = "unicode")]
pub fn word_indices(&self) -> WordIndices {
WordIndices::new(self)
}
#[cfg(feature = "unicode")]
pub fn words_with_breaks(&self) -> WordsWithBreaks {
WordsWithBreaks::new(self)
}
#[cfg(feature = "unicode")]
pub fn words_with_break_indices(&self) -> WordsWithBreakIndices {
WordsWithBreakIndices::new(self)
}
#[cfg(feature = "unicode")]
pub fn sentences(&self) -> Sentences {
Sentences::new(self)
}
#[cfg(feature = "unicode")]
pub fn sentence_indices(&self) -> SentenceIndices {
SentenceIndices::new(self)
}
pub fn lines(&self) -> Lines {
Lines::new(self)
}
pub fn lines_with_terminator(&self) -> LinesWithTerminator {
LinesWithTerminator::new(self)
}
#[cfg(feature = "unicode")]
pub fn trim(&self) -> &BStr {
self.trim_start().trim_end()
}
pub fn trim_start(&self) -> &BStr {
self.trim_start_imp()
}
#[cfg(feature = "unicode")]
fn trim_start_imp(&self) -> &BStr {
let start = whitespace_len_fwd(self.as_bytes());
&self[start..]
}
#[cfg(not(feature = "unicode"))]
fn trim_start_imp(&self) -> &BStr {
self.trim_start_with(|c| c.is_whitespace())
}
pub fn trim_end(&self) -> &BStr {
self.trim_end_imp()
}
#[cfg(feature = "unicode")]
fn trim_end_imp(&self) -> &BStr {
let end = whitespace_len_rev(self.as_bytes());
&self[..end]
}
#[cfg(not(feature = "unicode"))]
fn trim_end_imp(&self) -> &BStr {
self.trim_end_with(|c| c.is_whitespace())
}
pub fn trim_with<F: FnMut(char) -> bool>(&self, mut trim: F) -> &BStr {
self.trim_start_with(&mut trim).trim_end_with(&mut trim)
}
pub fn trim_start_with<F: FnMut(char) -> bool>(
&self,
mut trim: F,
) -> &BStr {
for (s, _, ch) in self.char_indices() {
if !trim(ch) {
return &self[s..];
}
}
B("")
}
pub fn trim_end_with<F: FnMut(char) -> bool>(
&self,
mut trim: F,
) -> &BStr {
for (_, e, ch) in self.char_indices().rev() {
if !trim(ch) {
return &self[..e];
}
}
B("")
}
#[cfg(all(feature = "std", feature = "unicode"))]
pub fn to_lowercase(&self) -> BString {
let mut buf = BString::new();
self.to_lowercase_into(&mut buf);
buf
}
#[cfg(all(feature = "std", feature = "unicode"))]
pub fn to_lowercase_into(&self, buf: &mut BString) {
buf.reserve(self.len());
for (s, e, ch) in self.char_indices() {
if ch == '\u{FFFD}' {
buf.push(&self[s..e]);
} else {
for upper in ch.to_lowercase() {
buf.push_char(upper);
}
}
}
}
#[cfg(feature = "std")]
pub fn to_ascii_lowercase(&self) -> BString {
BString::from(self.as_bytes().to_ascii_lowercase())
}
pub fn make_ascii_lowercase(&mut self) {
self.as_bytes_mut().make_ascii_lowercase();
}
#[cfg(all(feature = "std", feature = "unicode"))]
pub fn to_uppercase(&self) -> BString {
let mut buf = BString::new();
self.to_uppercase_into(&mut buf);
buf
}
#[cfg(all(feature = "std", feature = "unicode"))]
pub fn to_uppercase_into(&self, buf: &mut BString) {
buf.reserve(self.len());
for (s, e, ch) in self.char_indices() {
if ch == '\u{FFFD}' {
buf.push(&self[s..e]);
} else if ch.is_ascii() {
buf.push_char(ch.to_ascii_uppercase());
} else {
for upper in ch.to_uppercase() {
buf.push_char(upper);
}
}
}
}
#[cfg(feature = "std")]
pub fn to_ascii_uppercase(&self) -> BString {
BString::from(self.as_bytes().to_ascii_uppercase())
}
pub fn make_ascii_uppercase(&mut self) {
self.as_bytes_mut().make_ascii_uppercase();
}
pub fn reverse_bytes(&mut self) {
self.as_bytes_mut().reverse();
}
pub fn reverse_chars(&mut self) {
let mut i = 0;
loop {
let (_, size) = utf8::decode(self[i..].as_bytes());
if size == 0 {
break;
}
if size > 1 {
self[i..i + size].reverse_bytes();
}
i += size;
}
self.reverse_bytes();
}
#[cfg(feature = "unicode")]
pub fn reverse_graphemes(&mut self) {
use unicode::decode_grapheme;
let mut i = 0;
loop {
let (_, size) = decode_grapheme(&self[i..]);
if size == 0 {
break;
}
if size > 1 {
self[i..i + size].reverse_bytes();
}
i += size;
}
self.reverse_bytes();
}
pub fn is_ascii(&self) -> bool {
ascii::first_non_ascii_byte(&self.bytes) == self.len()
}
pub fn is_utf8(&self) -> bool {
utf8::validate(self.as_bytes()).is_ok()
}
pub fn split_at(&self, at: usize) -> (&BStr, &BStr) {
let (left, right) = self.as_bytes().split_at(at);
(BStr::new(left), BStr::new(right))
}
pub fn split_at_mut(&mut self, at: usize) -> (&mut BStr, &mut BStr) {
let (left, right) = self.as_bytes_mut().split_at_mut(at);
(BStr::new_mut(left), BStr::new_mut(right))
}
pub fn get<I: SliceIndex>(&self, at: I) -> Option<&I::Output> {
at.get(self)
}
pub fn get_mut<I: SliceIndex>(&mut self, at: I) -> Option<&mut I::Output> {
at.get_mut(self)
}
pub unsafe fn get_unchecked<I: SliceIndex>(&self, at: I) -> &I::Output {
at.get_unchecked(self)
}
pub unsafe fn get_unchecked_mut<I: SliceIndex>(
&mut self,
at: I,
) -> &mut I::Output {
at.get_unchecked_mut(self)
}
pub fn last(&self) -> Option<u8> {
self.get(self.len().saturating_sub(1)).map(|&b| b)
}
pub fn copy_within<R>(
&mut self,
src: R,
dest: usize,
) where R: ops::RangeBounds<usize>
{
let src_start = match src.start_bound() {
ops::Bound::Included(&n) => n,
ops::Bound::Excluded(&n) => {
n.checked_add(1).expect("attempted to index slice beyond max")
}
ops::Bound::Unbounded => 0,
};
let src_end = match src.end_bound() {
ops::Bound::Included(&n) => {
n.checked_add(1).expect("attempted to index slice beyond max")
}
ops::Bound::Excluded(&n) => n,
ops::Bound::Unbounded => self.len(),
};
assert!(src_start <= src_end, "src end is before src start");
assert!(src_end <= self.len(), "src is out of bounds");
let count = src_end - src_start;
assert!(dest <= self.len() - count, "dest is out of bounds");
unsafe {
ptr::copy(
self.get_unchecked(src_start),
self.get_unchecked_mut(dest),
count,
);
}
}
pub fn as_ptr(&self) -> *const u8 {
self.as_bytes().as_ptr()
}
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.as_bytes_mut().as_mut_ptr()
}
}
#[derive(Clone, Debug)]
pub struct Finder<'a> {
searcher: TwoWay<'a>,
}
impl<'a> Finder<'a> {
pub fn new<B: ?Sized + AsRef<[u8]>>(needle: &'a B) -> Finder<'a> {
Finder { searcher: TwoWay::forward(BStr::new(needle)) }
}
#[cfg(feature = "std")]
pub fn into_owned(self) -> Finder<'static> {
Finder { searcher: self.searcher.into_owned() }
}
pub fn needle(&self) -> &BStr {
self.searcher.needle()
}
pub fn find<B: AsRef<[u8]>>(&self, haystack: B) -> Option<usize> {
self.searcher.find(BStr::new(haystack.as_ref()))
}
}
#[derive(Clone, Debug)]
pub struct FinderReverse<'a> {
searcher: TwoWay<'a>,
}
impl<'a> FinderReverse<'a> {
pub fn new<B: ?Sized + AsRef<[u8]>>(needle: &'a B) -> FinderReverse<'a> {
FinderReverse { searcher: TwoWay::reverse(BStr::new(needle)) }
}
#[cfg(feature = "std")]
pub fn into_owned(self) -> FinderReverse<'static> {
FinderReverse { searcher: self.searcher.into_owned() }
}
pub fn needle(&self) -> &BStr {
self.searcher.needle()
}
pub fn rfind<B: AsRef<[u8]>>(&self, haystack: B) -> Option<usize> {
self.searcher.rfind(BStr::new(haystack.as_ref()))
}
}
#[derive(Debug)]
pub struct Find<'a> {
haystack: &'a BStr,
prestate: PrefilterState,
searcher: TwoWay<'a>,
pos: usize,
}
impl<'a> Find<'a> {
fn new(haystack: &'a BStr, needle: &'a BStr) -> Find<'a> {
let searcher = TwoWay::forward(needle);
let prestate = searcher.prefilter_state();
Find { haystack, prestate, searcher, pos: 0 }
}
}
impl<'a> Iterator for Find<'a> {
type Item = usize;
fn next(&mut self) -> Option<usize> {
if self.pos > self.haystack.len() {
return None;
}
let result = self.searcher.find_with(
&mut self.prestate,
&self.haystack[self.pos..],
);
match result {
None => None,
Some(i) => {
let pos = self.pos + i;
self.pos = pos + cmp::max(1, self.searcher.needle().len());
Some(pos)
}
}
}
}
#[derive(Debug)]
pub struct FindReverse<'a> {
haystack: &'a BStr,
prestate: PrefilterState,
searcher: TwoWay<'a>,
pos: Option<usize>,
}
impl<'a> FindReverse<'a> {
fn new(haystack: &'a BStr, needle: &'a BStr) -> FindReverse<'a> {
let searcher = TwoWay::reverse(needle);
let prestate = searcher.prefilter_state();
let pos = Some(haystack.len());
FindReverse { haystack, prestate, searcher, pos }
}
fn haystack(&self) -> &'a BStr {
self.haystack
}
fn needle(&self) -> &BStr {
self.searcher.needle()
}
}
impl<'a> Iterator for FindReverse<'a> {
type Item = usize;
fn next(&mut self) -> Option<usize> {
let pos = match self.pos {
None => return None,
Some(pos) => pos,
};
let result = self.searcher.rfind_with(
&mut self.prestate,
&self.haystack[..pos],
);
match result {
None => None,
Some(i) => {
if pos == i {
self.pos = pos.checked_sub(1);
} else {
self.pos = Some(i);
}
Some(i)
}
}
}
}
#[derive(Debug)]
pub struct Bytes<'a> {
it: slice::Iter<'a, u8>,
}
impl<'a> Iterator for Bytes<'a> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
self.it.next().map(|&b| b)
}
}
impl<'a> DoubleEndedIterator for Bytes<'a> {
fn next_back(&mut self) -> Option<u8> {
self.it.next_back().map(|&b| b)
}
}
impl<'a> ExactSizeIterator for Bytes<'a> {
fn len(&self) -> usize {
self.it.len()
}
}
#[derive(Debug)]
pub struct Fields<'a> {
it: FieldsWith<'a, fn(char) -> bool>,
}
impl<'a> Fields<'a> {
fn new(bytes: &'a BStr) -> Fields<'a> {
Fields { it: bytes.fields_with(|ch| ch.is_whitespace()) }
}
}
impl<'a> Iterator for Fields<'a> {
type Item = &'a BStr;
fn next(&mut self) -> Option<&'a BStr> {
self.it.next()
}
}
#[derive(Debug)]
pub struct FieldsWith<'a, F> {
f: F,
bytes: &'a BStr,
chars: CharIndices<'a>,
}
impl<'a, F: FnMut(char) -> bool> FieldsWith<'a, F> {
fn new(bytes: &'a BStr, f: F) -> FieldsWith<'a, F> {
FieldsWith {
f: f,
bytes: bytes,
chars: bytes.char_indices(),
}
}
}
impl<'a, F: FnMut(char) -> bool> Iterator for FieldsWith<'a, F> {
type Item = &'a BStr;
fn next(&mut self) -> Option<&'a BStr> {
let (start, mut end);
loop {
match self.chars.next() {
None => return None,
Some((s, e, ch)) => {
if !(self.f)(ch) {
start = s;
end = e;
break;
}
}
}
}
while let Some((_, e, ch)) = self.chars.next() {
if (self.f)(ch) {
break;
}
end = e;
}
Some(&self.bytes[start..end])
}
}
#[derive(Debug)]
pub struct Split<'a> {
finder: Find<'a>,
last: usize,
done: bool,
}
impl<'a> Split<'a> {
fn new(haystack: &'a BStr, splitter: &'a BStr) -> Split<'a> {
let finder = haystack.find_iter(splitter);
Split { finder, last: 0, done: false }
}
}
impl<'a> Iterator for Split<'a> {
type Item = &'a BStr;
fn next(&mut self) -> Option<&'a BStr> {
let haystack = self.finder.haystack;
match self.finder.next() {
Some(start) => {
let next = &haystack[self.last..start];
self.last = start + self.finder.searcher.needle().len();
Some(next)
}
None => {
if self.last >= haystack.len() {
if !self.done {
self.done = true;
Some(B(""))
} else {
None
}
} else {
let s = &haystack[self.last..];
self.last = haystack.len();
self.done = true;
Some(s)
}
}
}
}
}
#[derive(Debug)]
pub struct SplitReverse<'a> {
finder: FindReverse<'a>,
last: usize,
done: bool,
}
impl<'a> SplitReverse<'a> {
fn new(haystack: &'a BStr, splitter: &'a BStr) -> SplitReverse<'a> {
let finder = haystack.rfind_iter(splitter);
SplitReverse { finder, last: haystack.len(), done: false }
}
}
impl<'a> Iterator for SplitReverse<'a> {
type Item = &'a BStr;
fn next(&mut self) -> Option<&'a BStr> {
let haystack = self.finder.haystack();
match self.finder.next() {
Some(start) => {
let nlen = self.finder.needle().len();
let next = &haystack[start + nlen..self.last];
self.last = start;
Some(next)
}
None => {
if self.last == 0 {
if !self.done {
self.done = true;
Some(B(""))
} else {
None
}
} else {
let s = &haystack[..self.last];
self.last = 0;
self.done = true;
Some(s)
}
}
}
}
}
#[derive(Debug)]
pub struct SplitN<'a> {
split: Split<'a>,
limit: usize,
count: usize,
}
impl<'a> SplitN<'a> {
fn new(
haystack: &'a BStr,
splitter: &'a BStr,
limit: usize,
) -> SplitN<'a> {
let split = haystack.split(splitter);
SplitN { split, limit, count: 0 }
}
}
impl<'a> Iterator for SplitN<'a> {
type Item = &'a BStr;
fn next(&mut self) -> Option<&'a BStr> {
self.count += 1;
if self.count > self.limit {
None
} else if self.count == self.limit {
Some(&self.split.finder.haystack[self.split.last..])
} else {
self.split.next()
}
}
}
#[derive(Debug)]
pub struct SplitNReverse<'a> {
split: SplitReverse<'a>,
limit: usize,
count: usize,
}
impl<'a> SplitNReverse<'a> {
fn new(
haystack: &'a BStr,
splitter: &'a BStr,
limit: usize,
) -> SplitNReverse<'a> {
let split = haystack.rsplit(splitter);
SplitNReverse { split, limit, count: 0 }
}
}
impl<'a> Iterator for SplitNReverse<'a> {
type Item = &'a BStr;
fn next(&mut self) -> Option<&'a BStr> {
self.count += 1;
if self.count > self.limit {
None
} else if self.count == self.limit {
Some(&self.split.finder.haystack()[..self.split.last])
} else {
self.split.next()
}
}
}
pub struct Lines<'a> {
it: LinesWithTerminator<'a>,
}
impl<'a> Lines<'a> {
fn new(bytes: &'a BStr) -> Lines<'a> {
Lines { it: LinesWithTerminator::new(bytes) }
}
}
impl<'a> Iterator for Lines<'a> {
type Item = &'a BStr;
fn next(&mut self) -> Option<&'a BStr> {
let mut line = self.it.next()?;
if line.last() == Some(b'\n') {
line = &line[..line.len() - 1];
if line.last() == Some(b'\r') {
line = &line[..line.len() - 1];
}
}
Some(line)
}
}
pub struct LinesWithTerminator<'a> {
bytes: &'a BStr,
}
impl<'a> LinesWithTerminator<'a> {
fn new(bytes: &'a BStr) -> LinesWithTerminator<'a> {
LinesWithTerminator { bytes }
}
}
impl<'a> Iterator for LinesWithTerminator<'a> {
type Item = &'a BStr;
fn next(&mut self) -> Option<&'a BStr> {
match self.bytes.find_byte(b'\n') {
None if self.bytes.is_empty() => None,
None => {
let line = self.bytes;
self.bytes = B("");
Some(line)
}
Some(end) => {
let line = &self.bytes[..end + 1];
self.bytes = &self.bytes[end + 1..];
Some(line)
}
}
}
}
#[cfg(test)]
mod tests {
use tests::LOSSY_TESTS;
use super::*;
#[test]
fn to_str_lossy() {
for (i, &(expected, input)) in LOSSY_TESTS.iter().enumerate() {
let got = B(input).to_str_lossy();
assert_eq!(
expected.as_bytes(),
got.as_bytes(),
"to_str_lossy(ith: {:?}, given: {:?})",
i, input,
);
let mut got = String::new();
B(input).to_str_lossy_into(&mut got);
assert_eq!(
expected.as_bytes(), got.as_bytes(), "to_str_lossy_into",
);
let got = String::from_utf8_lossy(input);
assert_eq!(expected.as_bytes(), got.as_bytes(), "std");
}
}
#[test]
#[should_panic]
fn copy_within_fail1() {
let mut buf = *b"foobar";
let s = BStr::new_mut(&mut buf);
s.copy_within(0..2, 5);
}
#[test]
#[should_panic]
fn copy_within_fail2() {
let mut buf = *b"foobar";
let s = BStr::new_mut(&mut buf);
s.copy_within(3..2, 0);
}
#[test]
#[should_panic]
fn copy_within_fail3() {
let mut buf = *b"foobar";
let s = BStr::new_mut(&mut buf);
s.copy_within(5..7, 0);
}
#[test]
#[should_panic]
fn copy_within_fail4() {
let mut buf = *b"foobar";
let s = BStr::new_mut(&mut buf);
s.copy_within(0..1, 6);
}
}