shared_string/
lib.rs

1
2//! A `String` with shared ownership.
3//!
4//! Helpfull for some types that need to be parsed from a string
5//! and get split into smaller parts like an `Url` or a `Vec` containing lines
6//! which need to be owned by the parent type.
7//!
8//! ## Note
9//!
10//! First try to store references, for example `&str` which is more efficient.
11//!
12//! At the moment if you create a `SharedString` the underlying bytes cannot be
13//! mutated anymore.
14//!
15//! ## Example
16//!
17//! ```
18//! use shared_string::SharedString;
19//! // or SharedSyncString if `Sync` is required
20//!
21//! struct Name {
22//! 	firstname: SharedString,
23//! 	middlename: SharedString,
24//! 	lastname: SharedString
25//! 	// to be faster than string
26//! 	// you should use at least 3 fields
27//! }
28//!
29//! impl Name {
30//! 	pub fn new(fullname: impl Into<SharedString>) -> Option<Self> {
31//! 		let mut split = fullname.into().split(b' ');
32//! 		Some(Self {
33//! 			firstname: split.next()?,
34//! 			middlename: split.next()?,
35//! 			lastname: split.next()?
36//! 		})
37//! 	}
38//! }
39//!
40//! let name = Name::new("Bartholomew Jojo Simpson").unwrap();
41//! assert_eq!(name.firstname, "Bartholomew");
42//! assert_eq!(name.middlename, "Jojo");
43//! assert_eq!(name.lastname, "Simpson");
44//! ```
45//!
46//! ## Performance
47//!
48//! `SharedString` can increase the perfomance in situations such as the example
49//! above by over 30%. See `benches/*` for benchmarks.
50
51pub mod iter;
52use iter::{Split, Lines};
53
54use std::{ops, str, cmp, fmt, hash, borrow};
55use ops::Bound;
56use std::rc::Rc;
57use std::sync::Arc;
58use std::string::FromUtf8Error;
59
60/// A `SharedString`, generic over its reference counter.
61///
62/// Most likely you will only need to interact with the type definitions
63/// `SharedString` and `SharedSyncString`.
64///
65/// This struct is useful for parsers or other struct that hold a lot of strings
66/// which could all reference. For example a `Uri` Struct or
67/// if you have a string with many lines and need every line `independently`.
68///
69/// ## Lines example
70///
71/// ```
72/// use shared_string::SharedString;
73/// // or SharedSyncString if `Sync` is required
74///
75/// let lines: Vec<_> = SharedString::from("many\nlines\nmany").lines().collect();
76/// assert_eq!(lines[0], SharedString::from("many"));
77/// assert_eq!(lines[1], "lines");
78/// assert_eq!(lines.len(), 3);
79/// ```
80#[derive(Clone)]
81pub struct SharedGenString<R>
82where R: RefCounter {
83	// maybe replace start with a pointer??
84	start: usize,
85	len: usize,
86
87	// can only be generated from valid utf8
88	bytes: R
89}
90
91/// Use `SharedString` if you only need this type in one thread
92pub type SharedString = SharedGenString<Rc<Box<[u8]>>>;
93/// Use `SharedSyncString` if you need to pass it between threads
94pub type SharedSyncString = SharedGenString<Arc<Box<[u8]>>>;
95
96/// A trait to allow `SharedString` to be generic over any reference counter.
97///
98/// Implemented for `Rc` and `Arc`.
99///
100/// Requires the traits `Clone` + `Sized` +
101/// `Deref<Box<[u8]>>` + `From<Box<[u8]>>`
102pub trait RefCounter: Clone + Sized + ops::Deref<Target = Box<[u8]>> + From<Box<[u8]>> {
103	fn try_unwrap(self) -> Result<Box<[u8]>, Self>;
104}
105
106impl RefCounter for Rc<Box<[u8]>> {
107	#[inline]
108	fn try_unwrap(self) -> Result<Box<[u8]>, Self> {
109		Rc::try_unwrap(self)
110	}
111}
112
113impl RefCounter for Arc<Box<[u8]>> {
114	#[inline]
115	fn try_unwrap(self) -> Result<Box<[u8]>, Self> {
116		Arc::try_unwrap(self)
117	}
118}
119
120impl<R> SharedGenString<R>
121where R: RefCounter {
122
123	/// Creates a new empty `SharedString`.
124	#[inline]
125	pub fn new() -> Self {
126		"".into()
127	}
128
129	#[inline]
130	pub(crate) fn new_raw(start: usize, len: usize, bytes: R) -> Self {
131		Self { start, len, bytes }
132	}
133
134	/// Convert a vector of bytes to a `SharedString`.
135	///
136	/// Behaves the same way as [String::from_utf8](https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf8).
137	///
138	/// If you are sure that the bytes are valid UTF-8, there is an unsafe
139	/// method [from_utf8_unchecked](#method.from_utf8_unchecked) which behaves
140	/// the same way but skips the checks.
141	///
142	/// ## Errors
143	///
144	/// Returns an `FromUtf8Error` if the bytes are not valid UTF-8 with a
145	/// description were an invalid byte was found.
146	#[inline]
147	pub fn from_utf8(vec: Vec<u8>) -> Result<Self, FromUtf8Error> {
148		String::from_utf8(vec).map(|s| s.into())
149	}
150
151	/// Converts a vector of bytes to a `SharedString` with out checking that
152	/// every bytes is valid UTF-8.
153	///
154	/// Safe version [from_utf8](#method.from_utf8)
155	#[inline]
156	pub unsafe fn from_utf8_unchecked(vec: Vec<u8>) -> Self {
157		Self {
158			start: 0,
159			len: vec.len(),
160			bytes: vec.into_boxed_slice().into()
161		}
162	}
163
164	/// Returns a byte slice of the underlying bytes.
165	///
166	/// To get the full bytes from which this `SharedString` was created from
167	/// use [as_bytes_full](#method.as_bytes_full).
168	#[inline]
169	pub fn as_bytes(&self) -> &[u8] {
170		let end = self.start + self.len;
171		unsafe {
172			// Safe because we control start and end
173			// and know that it is not out-of-bounds
174			self.bytes.get_unchecked(self.start..end)
175		}
176	}
177
178	/// Return a byte slice of the bytes from which this `SharedString` was
179	/// created.
180	#[inline]
181	pub fn as_full_bytes(&self) -> &[u8] {
182		&self.bytes
183	}
184
185	/// Returns a string slice of the `SharedString`.
186	///
187	/// ## Example
188	///
189	/// ```
190	/// # use shared_string::SharedString;
191	/// let s = SharedString::from("foo");
192	///
193	/// assert_eq!("foo", s.as_str());
194	/// ```
195	#[inline]
196	pub fn as_str(&self) -> &str {
197		&self
198	}
199
200	/// Return a string slice of the bytes from which this `SharedString` was
201	/// created.
202	///
203	/// ## Example
204	///
205	/// ```
206	/// # use shared_string::SharedString;
207	/// let mut foo = SharedString::from("foobar");
208	/// let bar = foo.split_off(3);
209	///
210	/// assert_eq!("foo", foo.as_str());
211	/// assert_eq!("foobar", foo.as_full_str());
212	/// ```
213	#[inline]
214	pub fn as_full_str(&self) -> &str {
215		unsafe { str::from_utf8_unchecked(&self.bytes) }
216	}
217
218	/// Returns the len of `SharedString`.
219	///
220	/// ## Example
221	///
222	/// ```
223	/// # use shared_string::SharedString;
224	/// let mut foo = SharedString::from("foobar");
225	/// let bar = foo.split_off(3);
226	///
227	/// assert_eq!(3, foo.len());
228	/// assert_eq!(3, bar.len());
229	/// ```
230	#[inline]
231	pub fn len(&self) -> usize {
232		self.len
233	}
234
235	/// Returns `true` if the length is zero, and `false` otherwise.
236	#[inline]
237	pub fn is_empty(&self) -> bool {
238		self.len == 0
239	}
240
241	// returns new start and end if it is a valid range
242	// will be equal to x..y
243	// valid: start <= end && end <= len
244	#[inline]
245	fn validate_range<I>(&self, range: I) -> Option<(usize, usize)>
246	where I: ops::RangeBounds<usize> {
247
248		let len = self.len();
249
250		let start = match range.start_bound() {
251			Bound::Included(&i) => i,
252			Bound::Excluded(&i) => i + 1,
253			Bound::Unbounded => 0
254		};
255
256		let end = match range.end_bound() {
257			Bound::Included(&i) => i + 1,
258			Bound::Excluded(&i) => i,
259			Bound::Unbounded => len
260		};
261
262		if start > end || end > len {
263			None
264		} else {
265			Some((start, end))
266		}
267	}
268
269	/// Returns a substring of `SharedString`.
270	///
271	/// This is the non-panicking alternative to [idx](#method.idx) and returns
272	/// `None` if the range is out-of-bounds or if the start or the end are not
273	/// at a char_boundary.
274	///
275	/// No allocation is performed.
276	///
277	/// ## Example
278	///
279	/// ```
280	/// # use shared_string::SharedString;
281	/// # fn inner() -> Option<()> {
282	/// let foobar = SharedString::from("foobar");
283	///
284	/// assert_eq!("foo", foobar.get(..3)?);
285	/// assert_eq!("foob", foobar.get(..=3)?);
286	/// assert_eq!("foobar", foobar.get(..)?);
287	/// assert_eq!("bar", foobar.get(3..)?);
288	/// # None
289	/// # }
290	/// # inner();
291	/// ```
292	#[inline]
293	pub fn get<I>(&self, range: I) -> Option<Self>
294	where I: ops::RangeBounds<usize> {
295		let (start, end) = self.validate_range(range)?;
296
297		if start == end {
298			return Some(Self::new())
299		}
300
301		// should validate if is char boundary
302		let s = self.as_str();
303		if !(s.is_char_boundary(start) && s.is_char_boundary(end)) {
304			return None;
305		}
306
307		Some(Self {
308			start: self.start + start,
309			len: end - start,
310			bytes: self.bytes.clone()
311		})
312	}
313
314	/// Returns a substring of `SharedString` for which no allocation is
315	/// performed.
316	///
317	/// ## Panics
318	///
319	/// Panics if the range is out-of-bounds.
320	///
321	/// ## Warning
322	///
323	/// This method can lead to invalid utf8 if not "split" at a char boundary.
324	///
325	/// ## Note
326	///
327	/// The [Index](https://doc.rust-lang.org/std/ops/trait.Index.html) Trait
328	/// is not implemented because `index` always returns a reference
329	/// and here you always receive an owned type.
330	///
331	/// ## Example
332	///
333	/// ```
334	/// # use shared_string::SharedString;
335	/// let foobar = SharedString::from("foobar");
336	///
337	/// assert_eq!("foo", foobar.idx(..3));
338	/// assert_eq!("foob", foobar.idx(..=3));
339	/// assert_eq!("foobar", foobar.idx(..));
340	/// assert_eq!("bar", foobar.idx(3..));
341	/// ```
342	#[inline]
343	pub fn idx<I>(&self, range: I) -> Self
344	where I: ops::RangeBounds<usize> {
345		let (start, end) = self.validate_range(range).expect("invalid range");
346
347		Self {
348			start: self.start + start,
349			len: end - start,
350			bytes: self.bytes.clone()
351		}
352	}
353
354	/// Convert `SharedString` to a `Vec<u8>`.
355	///
356	/// Avoids an allocation if the underlying data is not used by another
357	/// instance of `SharedString` and start is at zero.
358	#[inline]
359	pub fn into_bytes(self) -> Vec<u8> {
360		match self.bytes.try_unwrap().map(|b| b.into_vec()) {
361			// don't allocate
362			Ok(mut bytes) if self.start == 0 => {
363				bytes.truncate(self.len);
364				bytes
365			},
366			// needs an allocation
367			// Safe because only we control self.start and self.end
368			Ok(bytes) => unsafe {
369				let range = self.start..(self.start + self.len);
370				bytes.get_unchecked(range).to_vec()
371			},
372			// needs an allocation
373			// Safe because only we control self.start and self.end
374			Err(slice) => unsafe {
375				let range = self.start..(self.start + self.len);
376				slice.get_unchecked(range).to_vec()
377			}
378		}
379	}
380
381	/// Returns the underlying Bytes from which this `SharedString` was created.
382	///
383	/// Tries to avoid a call to `clone` if the underlying data is not used
384	/// by another instance.
385	#[inline]
386	pub fn into_full_bytes(self) -> Vec<u8> {
387		match self.bytes.try_unwrap() {
388			Ok(bytes) => bytes.into(),
389			Err(slice) => slice.to_vec()
390		}
391	}
392
393	/// Convert `SharedString` to a `String`.
394	///
395	/// Tries to avoid a call to `clone` if the underlying data is not used
396	/// by another instance of `SharedString` and start is at zero.
397	#[inline]
398	pub fn into_string(self) -> String {
399		let vec = self.into_bytes();
400		// Safe because we know the bytes are valid UTF-8
401		unsafe { String::from_utf8_unchecked(vec) }
402	}
403
404	/// Returns the underlying Bytes as a `String` from which this
405	/// `SharedString` was created.
406	///
407	/// Tries to avoid a call to `clone` if the underlying data is not used
408	/// by another instance.
409	#[inline]
410	pub fn into_full_string(self) -> String {
411		let vec = self.into_full_bytes();
412		unsafe { String::from_utf8_unchecked(vec) }
413	}
414
415	/// Pushes a char to the `String` returned by
416	/// [into_string](#method.into_string).
417	///
418	/// If the conditions in `into_string` are met no `clone` is perfomed.
419	///
420	/// ## Example
421	///
422	/// ```
423	/// # use shared_string::SharedString;
424	/// let fooba = SharedString::from("fooba");
425	/// let foobar = fooba.push('r');
426	///
427	/// assert_eq!(foobar, "foobar");
428	/// ```
429	#[inline]
430	pub fn push(self, ch: char) -> String {
431		let mut s = self.into_string();
432		s.push(ch);
433		s
434	}
435
436	/// Pushes a string slice to the `String` returned by
437	/// [into_string](#method.into_string).
438	///
439	/// If the conditions in `into_string` are met no `clone` is perfomed.
440	///
441	/// ## Example
442	///
443	/// ```
444	/// # use shared_string::SharedString;
445	/// let foo = SharedString::from("foo");
446	/// let foobar = foo.push_str("bar");
447	///
448	/// assert_eq!(foobar, "foobar");
449	/// ```
450	#[inline]
451	pub fn push_str(self, string: &str) -> String {
452		let mut s = self.into_string();
453		s.push_str(string);
454		s
455	}
456
457	/// Splits the `SharedString` into two at the given index.
458	///
459	/// No allocation is needed.
460	///
461	/// This is `O(1)` because only the reference counter is increased.
462	///
463	/// ## Panics
464	///
465	/// Panics if `at` is not at a char boundary.
466	#[inline]
467	pub fn split_off(&mut self, at: usize) -> Self {
468		if at == 0 {
469			let c = self.clone();
470			self.len = 0;
471			return c
472		}
473
474		// panics if at > self.len
475		assert!(self.is_char_boundary(at));
476
477		let n_len = self.len - at;
478		self.len = at;
479
480		Self {
481			start: self.start + at,
482			len: n_len,
483			bytes: self.bytes.clone()
484		}
485	}
486
487	/// Returns an iterator which returns for every "segment" a `SharedString`.
488	///
489	/// At the moment only u8 as "splitter" is supported.
490	///
491	/// u8 will be replaced when [Pattern](https://doc.rust-lang.org/std/str/pattern/trait.Pattern.html) gets stabilized.
492	///
493	/// ## Example
494	///
495	/// ```
496	/// # use shared_string::SharedString;
497	/// let mut foobar = SharedString::from("foo bar").split(b' ');
498	/// let foo = foobar.next().unwrap();
499	/// let bar = foobar.next().unwrap();
500	///
501	/// assert_eq!(foo, "foo");
502	/// assert_eq!(bar, "bar");
503	/// ```
504	#[inline]
505	pub fn split(self, byte: u8) -> Split<R> {
506		Split::new(self.start, self.len, self.bytes, byte)
507	}
508
509	/// Returns an iterator which returns for every line a `SharedString`.
510	///
511	/// Be aware that this doens't behave exactly like [lines](#method.lines).
512	///
513	/// This implementation returns an empty line at the if there is a `\n`
514	/// byte.
515	///
516	/// ## Example
517	///
518	/// ```
519	/// # use shared_string::SharedString;
520	/// let mut lines = SharedString::from("foo\r\nbar\n\nbaz\n").lines();
521	///
522	/// assert_eq!("foo", lines.next().unwrap());
523	/// assert_eq!("bar", lines.next().unwrap());
524	/// assert_eq!("", lines.next().unwrap());
525	/// assert_eq!("baz", lines.next().unwrap());
526	///
527	/// assert_eq!(None, lines.next());
528	/// ```
529	#[inline]
530	pub fn lines(self) -> Lines<R> {
531		Lines::new(self.start, self.len, self.bytes)
532	}
533
534	/// Shortens this `SharedString` to the specified length.
535	///
536	/// If `new_len` is greater than the current length, nothing happens.
537	///
538	/// ## Panics
539	///
540	/// Panics if `new_len` does not lie on a char boundary.
541	#[inline]
542	pub fn truncate(&mut self, new_len: usize) {
543		if new_len < self.len {
544			assert!(self.is_char_boundary(new_len));
545			self.len = new_len;
546		}
547	}
548}
549
550impl<R> fmt::Display for SharedGenString<R>
551where R: RefCounter {
552	#[inline]
553	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
554		fmt::Display::fmt(self.as_str(), f)
555	}
556}
557
558impl<R> fmt::Debug for SharedGenString<R>
559where R: RefCounter {
560	#[inline]
561	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
562		fmt::Debug::fmt(self.as_str(), f)
563	}
564}
565
566impl<R> hash::Hash for SharedGenString<R>
567where R: RefCounter {
568	#[inline]
569	fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
570		self.as_str().hash(hasher)
571	}
572}
573
574impl<R> ops::Deref for SharedGenString<R>
575where R: RefCounter {
576	type Target = str;
577
578	#[inline]
579	fn deref(&self) -> &str {
580		// Safe because we know that Self contains valid utf8
581		unsafe { str::from_utf8_unchecked(self.as_bytes()) }
582	}
583}
584
585impl<R> AsRef<str> for SharedGenString<R>
586where R: RefCounter {
587	#[inline]
588	fn as_ref(&self) -> &str {
589		self
590	}
591}
592
593impl<R> borrow::Borrow<str> for SharedGenString<R>
594where R: RefCounter {
595	#[inline]
596	fn borrow(&self) -> &str {
597		self
598	}
599}
600
601// need a custom eq
602impl<R, O> cmp::PartialEq<SharedGenString<O>> for SharedGenString<R>
603where
604	R: RefCounter,
605	O: RefCounter {
606	#[inline]
607	fn eq(&self, other: &SharedGenString<O>) -> bool {
608		self.as_bytes() == other.as_bytes()
609	}
610}
611
612impl<R: RefCounter> cmp::Eq for SharedGenString<R> {}
613
614impl<R> cmp::PartialEq<str> for SharedGenString<R>
615where R: RefCounter {
616	#[inline]
617	fn eq(&self, other: &str) -> bool {
618		self.as_str() == other
619	}
620}
621
622impl<R> cmp::PartialEq<&str> for SharedGenString<R>
623where R: RefCounter {
624	#[inline]
625	fn eq(&self, other: &&str) -> bool {
626		self.as_str() == *other
627	}
628}
629
630impl cmp::PartialEq<SharedString> for str {
631	#[inline]
632	fn eq(&self, other: &SharedString) -> bool {
633		self == other.as_str()
634	}
635}
636
637impl cmp::PartialEq<SharedString> for &str {
638	#[inline]
639	fn eq(&self, other: &SharedString) -> bool {
640		*self == other.as_str()
641	}
642}
643
644impl cmp::PartialEq<SharedSyncString> for str {
645	#[inline]
646	fn eq(&self, other: &SharedSyncString) -> bool {
647		self == other.as_str()
648	}
649}
650
651impl cmp::PartialEq<SharedSyncString> for &str {
652	#[inline]
653	fn eq(&self, other: &SharedSyncString) -> bool {
654		*self == other.as_str()
655	}
656}
657
658// need a custom ord
659
660impl<R> Default for SharedGenString<R>
661where R: RefCounter {
662	/// Creates a new empty `SharedString`.
663	#[inline]
664	fn default() -> Self {
665		Self::new()
666	}
667}
668
669impl<R> From<String> for SharedGenString<R>
670where R: RefCounter {
671	#[inline]
672	fn from(s: String) -> Self {
673		Self {
674			start: 0,
675			len: s.len(),
676			bytes: s.into_bytes().into_boxed_slice().into()
677		}
678	}
679}
680
681impl<R> From<&str> for SharedGenString<R>
682where R: RefCounter {
683	#[inline]
684	fn from(s: &str) -> Self {
685		s.to_string().into()
686	}
687}
688
689impl<R> From<SharedGenString<R>> for String
690where R: RefCounter {
691	#[inline]
692	fn from(s: SharedGenString<R>) -> Self {
693		s.into_string()
694	}
695}
696
697// Tests
698#[cfg(test)]
699mod tests {
700
701	use super::{SharedString, SharedSyncString};
702
703	#[test]
704	fn rc() {
705		let mut hello: SharedString = "Hello, World!".into();
706		assert_eq!(hello.len(), 13);
707
708		let world = hello.split_off(7);
709		assert_eq!(hello, "Hello, ");
710		assert_eq!(hello.len(), 7);
711		assert_eq!(world, "World!");
712		assert_eq!(world.len(), 6);
713	}
714
715	#[test]
716	fn arc() {
717		let mut hello: SharedSyncString = "Hello, World!".into();
718
719		let world = hello.split_off(7);
720
721		std::thread::spawn(move || {
722			assert_eq!(world, "World!");
723			assert_eq!(world.as_full_str(), "Hello, World!");
724		});
725
726		assert_eq!(hello, "Hello, ");
727	}
728
729	#[test]
730	fn into() {
731		let hello = SharedString::from("Hello, World!");
732		let s = hello.into_string();
733		assert_eq!(s, "Hello, World!");
734
735		let mut hello: SharedString = s.into();
736		let world = hello.split_off(7);
737
738		let s = world.into_string();
739		assert_eq!(s, "World!");
740
741		assert!(hello != s.as_str());
742
743		let n_hello = SharedString::from("Hello, ");
744		assert_eq!(hello, n_hello);
745	}
746
747	#[test]
748	fn split_off_zero() {
749		let mut foobar = SharedString::from("foobar");
750		let n_foobar = foobar.split_off(0);
751		assert_eq!("", foobar);
752		assert_eq!("foobar", n_foobar);
753	}
754
755	#[test]
756	#[should_panic]
757	fn panic_char_boundary() {
758		let mut s = SharedString::from("abc 好 def");
759		let _ = s.split_off(5);
760	}
761
762	#[test]
763	#[should_panic]
764	fn panic_length() {
765		let mut s = SharedString::from("abc");
766		let _ = s.split_off(5);
767	}
768
769	#[test]
770	fn range_as_str() {
771		let raw = SharedString::from("Hello, World!");
772		let hello = &raw[..5];
773		let world = &raw[7..];
774
775		assert_eq!(hello, "Hello");
776		assert_eq!(world, "World!");
777	}
778
779	#[test]
780	fn range_with_get() {
781		let raw = SharedString::from("Hello, World!");
782		let hello = raw.get(..5).unwrap();
783		let world = raw.get(7..).unwrap();
784
785		assert_eq!(hello, "Hello");
786		assert_eq!(world, "World!");
787	}
788
789	#[test]
790	fn range_with_idx() {
791		let raw = SharedString::from("Hello, World!");
792		let hello = raw.idx(..5);
793		let world = raw.idx(7..);
794
795		assert_eq!(hello, "Hello");
796		assert_eq!(world, "World!");
797	}
798
799	#[test]
800	fn empty() {
801		let s = SharedString::from("");
802		assert_eq!(s.len(), 0);
803		assert!(s.is_empty());
804
805		assert!(s.get(..).is_some());
806		assert!(s.idx(..).is_empty());
807		assert!(s.get(1..).is_none());
808	}
809
810	#[test]
811	fn equal() {
812		let rc: SharedString = "Hello, World!".into();
813		let arc: SharedSyncString = "Hello, World!".into();
814		assert_eq!(rc, arc);
815	}
816
817	#[test]
818	fn split() {
819		let fullname = SharedString::from("Albert Einstein");
820		let mut split = fullname.split(b' ');
821		assert_eq!(split.next().unwrap(), "Albert");
822		assert_eq!(split.next().unwrap(), "Einstein");
823		assert_eq!(split.next(), None);
824	}
825
826	#[test]
827	fn lines() {
828		let quote = SharedString::from("Wenn die Menschen nur über das sprächen,\nwas sie begreifen,\r\ndann würde es sehr still auf der Welt sein.\n\r\n");
829		let mut lines = quote.lines();
830		assert_eq!(
831			lines.next().unwrap(),
832			"Wenn die Menschen nur über das sprächen,"
833		);
834		assert_eq!(lines.next().unwrap(), "was sie begreifen,");
835		assert_eq!(
836			lines.next().unwrap(),
837			"dann würde es sehr still auf der Welt sein."
838		);
839		assert_eq!(lines.next().unwrap(), "");
840		assert_eq!(lines.next(), None);
841
842		let empty = SharedString::from(" ");
843		let mut lines = empty.lines();
844		assert_eq!(" ", lines.next().unwrap());
845		assert_eq!(lines.next(), None);
846	}
847
848	#[test]
849	fn range_eq_str_range() {
850		let line = "foo: bar";
851		let at = line.find(':').unwrap();
852		let key = &line[..at];
853		let value = &line[(at + 2)..];
854
855		assert_eq!(key, "foo");
856		assert_eq!(value, "bar");
857
858		let line = SharedString::from(line);
859		let key = line.idx(..at);
860		let value = line.idx((at + 2)..);
861
862		assert_eq!(key, "foo");
863		assert_eq!(value, "bar");
864	}
865
866	#[test]
867	fn range_in_range() {
868		let line = "date: Mon, 30 Nov 2020 22:16:22 GMT\nserver: mw1271.eqiad.wmnet\nx-content-type-options: nosniff";
869		let mut lines = SharedString::from(line).lines();
870
871		let _ = lines.next().unwrap();
872		let line = lines.next().unwrap();
873
874		let at = line.find(':').unwrap();
875		assert_eq!(at, 6);
876
877		let key = line.idx(..at);
878		assert_eq!(key, "server");
879
880		let value = line.idx((at + 2)..);
881		assert_eq!(value, "mw1271.eqiad.wmnet");
882	}
883
884	#[test]
885	fn truncate() {
886		let mut foobar = SharedString::from("foobar");
887		foobar.truncate(3);
888		assert_eq!(foobar, "foo");
889	}
890}