diff options
| author | Dylan DPC <dylan.dpc@gmail.com> | 2020-01-17 18:53:38 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-01-17 18:53:38 +0530 |
| commit | d3589aa4ed63c321fe2fc94af70c7f352971745f (patch) | |
| tree | 0025e8c9a0259db872e951c1d0a1214abbc9deb7 /src/libcore/str | |
| parent | 2480c9eac15608591f58730aed27caac9c30b4c2 (diff) | |
| parent | 022a7de26f5cb386ec853eec3b7f44dcd516a6d1 (diff) | |
| download | rust-d3589aa4ed63c321fe2fc94af70c7f352971745f.tar.gz rust-d3589aa4ed63c321fe2fc94af70c7f352971745f.zip | |
Rollup merge of #66564 - foeb:66219-document-unsafe-sync-cell-str, r=Amanieu
Document unsafe blocks in core::{cell, str, sync}
Split from #66506 (issue #66219). Hopefully doing a chunk at a time is more manageable!
r? @RalfJung
Diffstat (limited to 'src/libcore/str')
| -rw-r--r-- | src/libcore/str/lossy.rs | 7 | ||||
| -rw-r--r-- | src/libcore/str/mod.rs | 69 | ||||
| -rw-r--r-- | src/libcore/str/pattern.rs | 13 |
3 files changed, 59 insertions, 30 deletions
diff --git a/src/libcore/str/lossy.rs b/src/libcore/str/lossy.rs index 8a1fb9de546..88b2bc551b7 100644 --- a/src/libcore/str/lossy.rs +++ b/src/libcore/str/lossy.rs @@ -3,8 +3,6 @@ use crate::fmt::{self, Write}; use crate::mem; use crate::str as core_str; -// ignore-tidy-undocumented-unsafe - /// Lossy UTF-8 string. #[unstable(feature = "str_internals", issue = "none")] pub struct Utf8Lossy { @@ -17,6 +15,7 @@ impl Utf8Lossy { } pub fn from_bytes(bytes: &[u8]) -> &Utf8Lossy { + // SAFETY: Both use the same memory layout, and UTF-8 correctness isn't required. unsafe { mem::transmute(bytes) } } @@ -60,6 +59,8 @@ impl<'a> Iterator for Utf8LossyChunksIter<'a> { while i < self.source.len() { let i_ = i; + // SAFETY: `i` starts at `0`, is less than `self.source.len()`, and + // only increases, so `0 <= i < self.source.len()`. let byte = unsafe { *self.source.get_unchecked(i) }; i += 1; @@ -69,6 +70,7 @@ impl<'a> Iterator for Utf8LossyChunksIter<'a> { macro_rules! error { () => {{ + // SAFETY: We have checked up to `i` that source is valid UTF-8. unsafe { let r = Utf8LossyChunk { valid: core_str::from_utf8_unchecked(&self.source[0..i_]), @@ -130,6 +132,7 @@ impl<'a> Iterator for Utf8LossyChunksIter<'a> { } let r = Utf8LossyChunk { + // SAFETY: We have checked that the entire source is valid UTF-8. valid: unsafe { core_str::from_utf8_unchecked(self.source) }, broken: &[], }; diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index ab771b1164b..5a7cddd4041 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1,5 +1,4 @@ // ignore-tidy-filelength -// ignore-tidy-undocumented-unsafe //! String manipulation. //! @@ -341,6 +340,7 @@ impl Utf8Error { #[stable(feature = "rust1", since = "1.0.0")] pub fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { run_utf8_validation(v)?; + // SAFETY: Just ran validation. Ok(unsafe { from_utf8_unchecked(v) }) } @@ -379,6 +379,7 @@ pub fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { #[stable(feature = "str_mut_extras", since = "1.20.0")] pub fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { run_utf8_validation(v)?; + // SAFETY: Just ran validation. Ok(unsafe { from_utf8_unchecked_mut(v) }) } @@ -581,7 +582,7 @@ impl<'a> Iterator for Chars<'a> { #[inline] fn next(&mut self) -> Option<char> { next_code_point(&mut self.iter).map(|ch| { - // str invariant says `ch` is a valid Unicode Scalar Value + // SAFETY: `str` invariant says `ch` is a valid Unicode Scalar Value. unsafe { char::from_u32_unchecked(ch) } }) } @@ -628,7 +629,7 @@ impl<'a> DoubleEndedIterator for Chars<'a> { #[inline] fn next_back(&mut self) -> Option<char> { next_code_point_reverse(&mut self.iter).map(|ch| { - // str invariant says `ch` is a valid Unicode Scalar Value + // SAFETY: `str` invariant says `ch` is a valid Unicode Scalar Value. unsafe { char::from_u32_unchecked(ch) } }) } @@ -658,6 +659,7 @@ impl<'a> Chars<'a> { #[stable(feature = "iter_to_slice", since = "1.4.0")] #[inline] pub fn as_str(&self) -> &'a str { + // SAFETY: `Chars` is only made from a str, which guarantees the iter is valid UTF-8. unsafe { from_utf8_unchecked(self.iter.as_slice()) } } } @@ -1102,6 +1104,7 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { fn get_end(&mut self) -> Option<&'a str> { if !self.finished && (self.allow_trailing_empty || self.end - self.start > 0) { self.finished = true; + // SAFETY: `self.start` and `self.end` always lie on unicode boundaries. unsafe { let string = self.matcher.haystack().get_unchecked(self.start..self.end); Some(string) @@ -1119,6 +1122,7 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { let haystack = self.matcher.haystack(); match self.matcher.next_match() { + // SAFETY: `Searcher` guarantees that `a` and `b` lie on unicode boundaries. Some((a, b)) => unsafe { let elt = haystack.get_unchecked(self.start..a); self.start = b; @@ -1151,11 +1155,13 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { let haystack = self.matcher.haystack(); match self.matcher.next_match_back() { + // SAFETY: `Searcher` guarantees that `a` and `b` lie on unicode boundaries. Some((a, b)) => unsafe { let elt = haystack.get_unchecked(b..self.end); self.end = a; Some(elt) }, + // SAFETY: `self.start` and `self.end` always lie on unicode boundaries. None => unsafe { self.finished = true; Some(haystack.get_unchecked(self.start..self.end)) @@ -1297,6 +1303,7 @@ impl<'a, P: Pattern<'a>> MatchIndicesInternal<'a, P> { fn next(&mut self) -> Option<(usize, &'a str)> { self.0 .next_match() + // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. .map(|(start, end)| unsafe { (start, self.0.haystack().get_unchecked(start..end)) }) } @@ -1307,6 +1314,7 @@ impl<'a, P: Pattern<'a>> MatchIndicesInternal<'a, P> { { self.0 .next_match_back() + // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. .map(|(start, end)| unsafe { (start, self.0.haystack().get_unchecked(start..end)) }) } } @@ -1348,6 +1356,7 @@ where impl<'a, P: Pattern<'a>> MatchesInternal<'a, P> { #[inline] fn next(&mut self) -> Option<&'a str> { + // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. self.0.next_match().map(|(a, b)| unsafe { // Indices are known to be on utf8 boundaries self.0.haystack().get_unchecked(a..b) @@ -1359,6 +1368,7 @@ impl<'a, P: Pattern<'a>> MatchesInternal<'a, P> { where P::Searcher: ReverseSearcher<'a>, { + // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. self.0.next_match_back().map(|(a, b)| unsafe { // Indices are known to be on utf8 boundaries self.0.haystack().get_unchecked(a..b) @@ -1579,6 +1589,10 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { if align != usize::max_value() && align.wrapping_sub(index) % usize_bytes == 0 { let ptr = v.as_ptr(); while index < blocks_end { + // SAFETY: since `align - index` and `ascii_block_size` are + // multiples of `usize_bytes`, `block = ptr.add(index)` is + // always aligned with a `usize` so it's safe to dereference + // both `block` and `block.offset(1)`. unsafe { let block = ptr.add(index) as *const usize; // break if there is a nonascii byte @@ -1804,6 +1818,7 @@ mod traits { && slice.is_char_boundary(self.start) && slice.is_char_boundary(self.end) { + // SAFETY: just checked that `start` and `end` are on a char boundary. Some(unsafe { self.get_unchecked(slice) }) } else { None @@ -1815,6 +1830,7 @@ mod traits { && slice.is_char_boundary(self.start) && slice.is_char_boundary(self.end) { + // SAFETY: just checked that `start` and `end` are on a char boundary. Some(unsafe { self.get_unchecked_mut(slice) }) } else { None @@ -1845,6 +1861,7 @@ mod traits { && slice.is_char_boundary(self.start) && slice.is_char_boundary(self.end) { + // SAFETY: just checked that `start` and `end` are on a char boundary. unsafe { self.get_unchecked_mut(slice) } } else { super::slice_error_fail(slice, self.start, self.end) @@ -1873,6 +1890,7 @@ mod traits { #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { if slice.is_char_boundary(self.end) { + // SAFETY: just checked that `end` is on a char boundary. Some(unsafe { self.get_unchecked(slice) }) } else { None @@ -1881,6 +1899,7 @@ mod traits { #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { if slice.is_char_boundary(self.end) { + // SAFETY: just checked that `end` is on a char boundary. Some(unsafe { self.get_unchecked_mut(slice) }) } else { None @@ -1903,8 +1922,8 @@ mod traits { } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - // is_char_boundary checks that the index is in [0, .len()] if slice.is_char_boundary(self.end) { + // SAFETY: just checked that `end` is on a char boundary. unsafe { self.get_unchecked_mut(slice) } } else { super::slice_error_fail(slice, 0, self.end) @@ -1934,6 +1953,7 @@ mod traits { #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { if slice.is_char_boundary(self.start) { + // SAFETY: just checked that `start` is on a char boundary. Some(unsafe { self.get_unchecked(slice) }) } else { None @@ -1942,6 +1962,7 @@ mod traits { #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { if slice.is_char_boundary(self.start) { + // SAFETY: just checked that `start` is on a char boundary. Some(unsafe { self.get_unchecked_mut(slice) }) } else { None @@ -1966,8 +1987,8 @@ mod traits { } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - // is_char_boundary checks that the index is in [0, .len()] if slice.is_char_boundary(self.start) { + // SAFETY: just checked that `start` is on a char boundary. unsafe { self.get_unchecked_mut(slice) } } else { super::slice_error_fail(slice, self.start, slice.len()) @@ -2238,7 +2259,6 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "str_as_bytes", since = "1.32.0")] #[inline(always)] - // SAFETY: const sound because we transmute two types with the same layout #[allow(unused_attributes)] #[allow_internal_unstable(const_fn_union)] pub const fn as_bytes(&self) -> &[u8] { @@ -2247,6 +2267,7 @@ impl str { str: &'a str, slice: &'a [u8], } + // SAFETY: const sound because we transmute two types with the same layout unsafe { Slices { str: self }.slice } } @@ -2573,6 +2594,7 @@ impl str { pub fn split_at(&self, mid: usize) -> (&str, &str) { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { + // SAFETY: just checked that `mid` is on a char boundary. unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) } } else { slice_error_fail(self, 0, mid) @@ -2617,6 +2639,7 @@ impl str { if self.is_char_boundary(mid) { let len = self.len(); let ptr = self.as_mut_ptr(); + // SAFETY: just checked that `mid` is on a char boundary. unsafe { ( from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)), @@ -3805,10 +3828,8 @@ impl str { if let Some((_, b)) = matcher.next_reject_back() { j = b; } - unsafe { - // Searcher is known to return valid indices - self.get_unchecked(i..j) - } + // SAFETY: `Searcher` is known to return valid indices. + unsafe { self.get_unchecked(i..j) } } /// Returns a string slice with all prefixes that match a pattern @@ -3844,10 +3865,8 @@ impl str { if let Some((a, _)) = matcher.next_reject() { i = a; } - unsafe { - // Searcher is known to return valid indices - self.get_unchecked(i..self.len()) - } + // SAFETY: `Searcher` is known to return valid indices. + unsafe { self.get_unchecked(i..self.len()) } } /// Returns a string slice with the prefix removed. @@ -3878,10 +3897,8 @@ impl str { "The first search step from Searcher \ must include the first character" ); - unsafe { - // Searcher is known to return valid indices. - Some(self.get_unchecked(len..)) - } + // SAFETY: `Searcher` is known to return valid indices. + unsafe { Some(self.get_unchecked(len..)) } } else { None } @@ -3919,10 +3936,8 @@ impl str { "The first search step from ReverseSearcher \ must include the last character" ); - unsafe { - // Searcher is known to return valid indices. - Some(self.get_unchecked(..start)) - } + // SAFETY: `Searcher` is known to return valid indices. + unsafe { Some(self.get_unchecked(..start)) } } else { None } @@ -3970,10 +3985,8 @@ impl str { if let Some((_, b)) = matcher.next_reject_back() { j = b; } - unsafe { - // Searcher is known to return valid indices - self.get_unchecked(0..j) - } + // SAFETY: `Searcher` is known to return valid indices. + unsafe { self.get_unchecked(0..j) } } /// Returns a string slice with all prefixes that match a pattern @@ -4166,6 +4179,7 @@ impl str { /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] pub fn make_ascii_uppercase(&mut self) { + // SAFETY: safe because we transmute two types with the same layout. let me = unsafe { self.as_bytes_mut() }; me.make_ascii_uppercase() } @@ -4191,6 +4205,7 @@ impl str { /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] pub fn make_ascii_lowercase(&mut self) { + // SAFETY: safe because we transmute two types with the same layout. let me = unsafe { self.as_bytes_mut() }; me.make_ascii_lowercase() } @@ -4357,6 +4372,7 @@ impl Default for &str { impl Default for &mut str { /// Creates an empty mutable str fn default() -> Self { + // SAFETY: The empty string is valid UTF-8. unsafe { from_utf8_unchecked_mut(&mut []) } } } @@ -4412,6 +4428,7 @@ impl_fn_for_zst! { #[derive(Clone)] struct UnsafeBytesToStr impl<'a> Fn = |bytes: &'a [u8]| -> &'a str { + // SAFETY: not safe unsafe { from_utf8_unchecked(bytes) } }; } diff --git a/src/libcore/str/pattern.rs b/src/libcore/str/pattern.rs index 46d9499394a..ef64d8b0fdf 100644 --- a/src/libcore/str/pattern.rs +++ b/src/libcore/str/pattern.rs @@ -3,8 +3,6 @@ //! For more details, see the traits [`Pattern`], [`Searcher`], //! [`ReverseSearcher`], and [`DoubleEndedSearcher`]. -// ignore-tidy-undocumented-unsafe - #![unstable( feature = "pattern", reason = "API not fully fleshed out and ready to be stabilized", @@ -271,6 +269,14 @@ unsafe impl<'a> Searcher<'a> for CharSearcher<'a> { #[inline] fn next(&mut self) -> SearchStep { let old_finger = self.finger; + // SAFETY: 1-4 guarantee safety of `get_unchecked` + // 1. `self.finger` and `self.finger_back` are kept on unicode boundaries + // (this is invariant) + // 2. `self.finger >= 0` since it starts at 0 and only increases + // 3. `self.finger < self.finger_back` because otherwise the char `iter` + // would return `SearchStep::Done` + // 4. `self.finger` comes before the end of the haystack because `self.finger_back` + // starts at the end and only decreases let slice = unsafe { self.haystack.get_unchecked(old_finger..self.finger_back) }; let mut iter = slice.chars(); let old_len = iter.iter.len(); @@ -293,6 +299,7 @@ unsafe impl<'a> Searcher<'a> for CharSearcher<'a> { // get the haystack after the last character found let bytes = self.haystack.as_bytes().get(self.finger..self.finger_back)?; // the last byte of the utf8 encoded needle + // SAFETY: we have an invariant that `utf8_size < 5` let last_byte = unsafe { *self.utf8_encoded.get_unchecked(self.utf8_size - 1) }; if let Some(index) = memchr::memchr(last_byte, bytes) { // The new finger is the index of the byte we found, @@ -336,6 +343,7 @@ unsafe impl<'a> ReverseSearcher<'a> for CharSearcher<'a> { #[inline] fn next_back(&mut self) -> SearchStep { let old_finger = self.finger_back; + // SAFETY: see the comment for next() above let slice = unsafe { self.haystack.get_unchecked(self.finger..old_finger) }; let mut iter = slice.chars(); let old_len = iter.iter.len(); @@ -363,6 +371,7 @@ unsafe impl<'a> ReverseSearcher<'a> for CharSearcher<'a> { return None; }; // the last byte of the utf8 encoded needle + // SAFETY: we have an invariant that `utf8_size < 5` let last_byte = unsafe { *self.utf8_encoded.get_unchecked(self.utf8_size - 1) }; if let Some(index) = memchr::memrchr(last_byte, bytes) { // we searched a slice that was offset by self.finger, |
