summaryrefslogtreecommitdiffstats
path: root/vendor/utf-8/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/utf-8/src
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/utf-8/src')
-rw-r--r--vendor/utf-8/src/lib.rs186
-rw-r--r--vendor/utf-8/src/lossy.rs92
-rw-r--r--vendor/utf-8/src/read.rs167
3 files changed, 445 insertions, 0 deletions
diff --git a/vendor/utf-8/src/lib.rs b/vendor/utf-8/src/lib.rs
new file mode 100644
index 000000000..ec223f209
--- /dev/null
+++ b/vendor/utf-8/src/lib.rs
@@ -0,0 +1,186 @@
+mod lossy;
+mod read;
+
+pub use lossy::LossyDecoder;
+pub use read::{BufReadDecoder, BufReadDecoderError};
+
+use std::cmp;
+use std::error::Error;
+use std::fmt;
+use std::str;
+
+/// The replacement character, U+FFFD. In lossy decoding, insert it for every decoding error.
+pub const REPLACEMENT_CHARACTER: &'static str = "\u{FFFD}";
+
+#[derive(Debug, Copy, Clone)]
+pub enum DecodeError<'a> {
+ /// In lossy decoding insert `valid_prefix`, then `"\u{FFFD}"`,
+ /// then call `decode()` again with `remaining_input`.
+ Invalid {
+ valid_prefix: &'a str,
+ invalid_sequence: &'a [u8],
+ remaining_input: &'a [u8],
+ },
+
+ /// Call the `incomplete_suffix.try_complete` method with more input when available.
+ /// If no more input is available, this is an invalid byte sequence.
+ Incomplete {
+ valid_prefix: &'a str,
+ incomplete_suffix: Incomplete,
+ },
+}
+
+impl<'a> fmt::Display for DecodeError<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ DecodeError::Invalid {
+ valid_prefix,
+ invalid_sequence,
+ remaining_input,
+ } => write!(
+ f,
+ "found invalid byte sequence {invalid_sequence:02x?} after \
+ {valid_byte_count} valid bytes, followed by {unprocessed_byte_count} more \
+ unprocessed bytes",
+ invalid_sequence = invalid_sequence,
+ valid_byte_count = valid_prefix.len(),
+ unprocessed_byte_count = remaining_input.len()
+ ),
+ DecodeError::Incomplete {
+ valid_prefix,
+ incomplete_suffix,
+ } => write!(
+ f,
+ "found incomplete byte sequence {incomplete_suffix:02x?} after \
+ {valid_byte_count} bytes",
+ incomplete_suffix = incomplete_suffix,
+ valid_byte_count = valid_prefix.len()
+ ),
+ }
+ }
+}
+
+impl<'a> Error for DecodeError<'a> {}
+
+#[derive(Debug, Copy, Clone)]
+pub struct Incomplete {
+ pub buffer: [u8; 4],
+ pub buffer_len: u8,
+}
+
+pub fn decode(input: &[u8]) -> Result<&str, DecodeError> {
+ let error = match str::from_utf8(input) {
+ Ok(valid) => return Ok(valid),
+ Err(error) => error,
+ };
+
+ // FIXME: separate function from here to guide inlining?
+ let (valid, after_valid) = input.split_at(error.valid_up_to());
+ let valid = unsafe {
+ str::from_utf8_unchecked(valid)
+ };
+
+ match error.error_len() {
+ Some(invalid_sequence_length) => {
+ let (invalid, rest) = after_valid.split_at(invalid_sequence_length);
+ Err(DecodeError::Invalid {
+ valid_prefix: valid,
+ invalid_sequence: invalid,
+ remaining_input: rest
+ })
+ }
+ None => {
+ Err(DecodeError::Incomplete {
+ valid_prefix: valid,
+ incomplete_suffix: Incomplete::new(after_valid),
+ })
+ }
+ }
+}
+
+impl Incomplete {
+ pub fn empty() -> Self {
+ Incomplete {
+ buffer: [0, 0, 0, 0],
+ buffer_len: 0,
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.buffer_len == 0
+ }
+
+ pub fn new(bytes: &[u8]) -> Self {
+ let mut buffer = [0, 0, 0, 0];
+ let len = bytes.len();
+ buffer[..len].copy_from_slice(bytes);
+ Incomplete {
+ buffer: buffer,
+ buffer_len: len as u8,
+ }
+ }
+
+ /// * `None`: still incomplete, call `try_complete` again with more input.
+ /// If no more input is available, this is invalid byte sequence.
+ /// * `Some((result, remaining_input))`: We’re done with this `Incomplete`.
+ /// To keep decoding, pass `remaining_input` to `decode()`.
+ pub fn try_complete<'input>(&mut self, input: &'input [u8])
+ -> Option<(Result<&str, &[u8]>, &'input [u8])> {
+ let (consumed, opt_result) = self.try_complete_offsets(input);
+ let result = opt_result?;
+ let remaining_input = &input[consumed..];
+ let result_bytes = self.take_buffer();
+ let result = match result {
+ Ok(()) => Ok(unsafe { str::from_utf8_unchecked(result_bytes) }),
+ Err(()) => Err(result_bytes),
+ };
+ Some((result, remaining_input))
+ }
+
+ fn take_buffer(&mut self) -> &[u8] {
+ let len = self.buffer_len as usize;
+ self.buffer_len = 0;
+ &self.buffer[..len as usize]
+ }
+
+ /// (consumed_from_input, None): not enough input
+ /// (consumed_from_input, Some(Err(()))): error bytes in buffer
+ /// (consumed_from_input, Some(Ok(()))): UTF-8 string in buffer
+ fn try_complete_offsets(&mut self, input: &[u8]) -> (usize, Option<Result<(), ()>>) {
+ let initial_buffer_len = self.buffer_len as usize;
+ let copied_from_input;
+ {
+ let unwritten = &mut self.buffer[initial_buffer_len..];
+ copied_from_input = cmp::min(unwritten.len(), input.len());
+ unwritten[..copied_from_input].copy_from_slice(&input[..copied_from_input]);
+ }
+ let spliced = &self.buffer[..initial_buffer_len + copied_from_input];
+ match str::from_utf8(spliced) {
+ Ok(_) => {
+ self.buffer_len = spliced.len() as u8;
+ (copied_from_input, Some(Ok(())))
+ }
+ Err(error) => {
+ let valid_up_to = error.valid_up_to();
+ if valid_up_to > 0 {
+ let consumed = valid_up_to.checked_sub(initial_buffer_len).unwrap();
+ self.buffer_len = valid_up_to as u8;
+ (consumed, Some(Ok(())))
+ } else {
+ match error.error_len() {
+ Some(invalid_sequence_length) => {
+ let consumed = invalid_sequence_length
+ .checked_sub(initial_buffer_len).unwrap();
+ self.buffer_len = invalid_sequence_length as u8;
+ (consumed, Some(Err(())))
+ }
+ None => {
+ self.buffer_len = spliced.len() as u8;
+ (copied_from_input, None)
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/utf-8/src/lossy.rs b/vendor/utf-8/src/lossy.rs
new file mode 100644
index 000000000..00bcdecf0
--- /dev/null
+++ b/vendor/utf-8/src/lossy.rs
@@ -0,0 +1,92 @@
+use super::*;
+
+/// A push-based, lossy decoder for UTF-8.
+/// Errors are replaced with the U+FFFD replacement character.
+///
+/// Users “push” bytes into the decoder, which in turn “pushes” `&str` slices into a callback.
+///
+/// For example, `String::from_utf8_lossy` (but returning `String` instead of `Cow`)
+/// can be rewritten as:
+///
+/// ```rust
+/// fn string_from_utf8_lossy(input: &[u8]) -> String {
+/// let mut string = String::new();
+/// utf8::LossyDecoder::new(|s| string.push_str(s)).feed(input);
+/// string
+/// }
+/// ```
+///
+/// **Note:** Dropping the decoder signals the end of the input:
+/// If the last input chunk ended with an incomplete byte sequence for a code point,
+/// this is an error and a replacement character is emitted.
+/// Use `std::mem::forget` to inhibit this behavior.
+pub struct LossyDecoder<F: FnMut(&str)> {
+ push_str: F,
+ incomplete: Incomplete,
+}
+
+impl<F: FnMut(&str)> LossyDecoder<F> {
+ /// Create a new decoder from a callback.
+ #[inline]
+ pub fn new(push_str: F) -> Self {
+ LossyDecoder {
+ push_str: push_str,
+ incomplete: Incomplete {
+ buffer: [0, 0, 0, 0],
+ buffer_len: 0,
+ },
+ }
+ }
+
+ /// Feed one chunk of input into the decoder.
+ ///
+ /// The input is decoded lossily
+ /// and the callback called once or more with `&str` string slices.
+ ///
+ /// If the UTF-8 byte sequence for one code point was split into this bytes chunk
+ /// and previous bytes chunks, it will be correctly pieced back together.
+ pub fn feed(&mut self, mut input: &[u8]) {
+ if self.incomplete.buffer_len > 0 {
+ match self.incomplete.try_complete(input) {
+ Some((Ok(s), remaining)) => {
+ (self.push_str)(s);
+ input = remaining
+ }
+ Some((Err(_), remaining)) => {
+ (self.push_str)(REPLACEMENT_CHARACTER);
+ input = remaining
+ }
+ None => {
+ return
+ }
+ }
+ }
+ loop {
+ match decode(input) {
+ Ok(s) => {
+ (self.push_str)(s);
+ return
+ }
+ Err(DecodeError::Incomplete { valid_prefix, incomplete_suffix }) => {
+ (self.push_str)(valid_prefix);
+ self.incomplete = incomplete_suffix;
+ return
+ }
+ Err(DecodeError::Invalid { valid_prefix, remaining_input, .. }) => {
+ (self.push_str)(valid_prefix);
+ (self.push_str)(REPLACEMENT_CHARACTER);
+ input = remaining_input
+ }
+ }
+ }
+ }
+}
+
+impl<F: FnMut(&str)> Drop for LossyDecoder<F> {
+ #[inline]
+ fn drop(&mut self) {
+ if self.incomplete.buffer_len > 0 {
+ (self.push_str)(REPLACEMENT_CHARACTER)
+ }
+ }
+}
diff --git a/vendor/utf-8/src/read.rs b/vendor/utf-8/src/read.rs
new file mode 100644
index 000000000..5e38f54a1
--- /dev/null
+++ b/vendor/utf-8/src/read.rs
@@ -0,0 +1,167 @@
+use std::io::{self, BufRead};
+use std::error::Error;
+use std::fmt;
+use std::str;
+use super::*;
+
+/// Wraps a `std::io::BufRead` buffered byte stream and decode it as UTF-8.
+pub struct BufReadDecoder<B: BufRead> {
+ buf_read: B,
+ bytes_consumed: usize,
+ incomplete: Incomplete,
+}
+
+#[derive(Debug)]
+pub enum BufReadDecoderError<'a> {
+ /// Represents one UTF-8 error in the byte stream.
+ ///
+ /// In lossy decoding, each such error should be replaced with U+FFFD.
+ /// (See `BufReadDecoder::next_lossy` and `BufReadDecoderError::lossy`.)
+ InvalidByteSequence(&'a [u8]),
+
+ /// An I/O error from the underlying byte stream
+ Io(io::Error),
+}
+
+impl<'a> BufReadDecoderError<'a> {
+ /// Replace UTF-8 errors with U+FFFD
+ pub fn lossy(self) -> Result<&'static str, io::Error> {
+ match self {
+ BufReadDecoderError::Io(error) => Err(error),
+ BufReadDecoderError::InvalidByteSequence(_) => Ok(REPLACEMENT_CHARACTER),
+ }
+ }
+}
+
+impl<'a> fmt::Display for BufReadDecoderError<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ BufReadDecoderError::InvalidByteSequence(bytes) => {
+ write!(f, "invalid byte sequence: {:02x?}", bytes)
+ }
+ BufReadDecoderError::Io(ref err) => write!(f, "underlying bytestream error: {}", err),
+ }
+ }
+}
+
+impl<'a> Error for BufReadDecoderError<'a> {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match *self {
+ BufReadDecoderError::InvalidByteSequence(_) => None,
+ BufReadDecoderError::Io(ref err) => Some(err),
+ }
+ }
+}
+
+impl<B: BufRead> BufReadDecoder<B> {
+ /// This is to `Read::read_to_string` what `String::from_utf8_lossy` is to `String::from_utf8`.
+ pub fn read_to_string_lossy(buf_read: B) -> io::Result<String> {
+ let mut decoder = Self::new(buf_read);
+ let mut string = String::new();
+ while let Some(result) = decoder.next_lossy() {
+ string.push_str(result?)
+ }
+ Ok(string)
+ }
+
+ pub fn new(buf_read: B) -> Self {
+ Self {
+ buf_read,
+ bytes_consumed: 0,
+ incomplete: Incomplete::empty(),
+ }
+ }
+
+ /// Same as `BufReadDecoder::next_strict`, but replace UTF-8 errors with U+FFFD.
+ pub fn next_lossy(&mut self) -> Option<io::Result<&str>> {
+ self.next_strict().map(|result| result.or_else(|e| e.lossy()))
+ }
+
+ /// Decode and consume the next chunk of UTF-8 input.
+ ///
+ /// This method is intended to be called repeatedly until it returns `None`,
+ /// which represents EOF from the underlying byte stream.
+ /// This is similar to `Iterator::next`,
+ /// except that decoded chunks borrow the decoder (~iterator)
+ /// so they need to be handled or copied before the next chunk can start decoding.
+ pub fn next_strict(&mut self) -> Option<Result<&str, BufReadDecoderError>> {
+ enum BytesSource {
+ BufRead(usize),
+ Incomplete,
+ }
+ macro_rules! try_io {
+ ($io_result: expr) => {
+ match $io_result {
+ Ok(value) => value,
+ Err(error) => return Some(Err(BufReadDecoderError::Io(error)))
+ }
+ }
+ }
+ let (source, result) = loop {
+ if self.bytes_consumed > 0 {
+ self.buf_read.consume(self.bytes_consumed);
+ self.bytes_consumed = 0;
+ }
+ let buf = try_io!(self.buf_read.fill_buf());
+
+ // Force loop iteration to go through an explicit `continue`
+ enum Unreachable {}
+ let _: Unreachable = if self.incomplete.is_empty() {
+ if buf.is_empty() {
+ return None // EOF
+ }
+ match str::from_utf8(buf) {
+ Ok(_) => {
+ break (BytesSource::BufRead(buf.len()), Ok(()))
+ }
+ Err(error) => {
+ let valid_up_to = error.valid_up_to();
+ if valid_up_to > 0 {
+ break (BytesSource::BufRead(valid_up_to), Ok(()))
+ }
+ match error.error_len() {
+ Some(invalid_sequence_length) => {
+ break (BytesSource::BufRead(invalid_sequence_length), Err(()))
+ }
+ None => {
+ self.bytes_consumed = buf.len();
+ self.incomplete = Incomplete::new(buf);
+ // need more input bytes
+ continue
+ }
+ }
+ }
+ }
+ } else {
+ if buf.is_empty() {
+ break (BytesSource::Incomplete, Err(())) // EOF with incomplete code point
+ }
+ let (consumed, opt_result) = self.incomplete.try_complete_offsets(buf);
+ self.bytes_consumed = consumed;
+ match opt_result {
+ None => {
+ // need more input bytes
+ continue
+ }
+ Some(result) => {
+ break (BytesSource::Incomplete, result)
+ }
+ }
+ };
+ };
+ let bytes = match source {
+ BytesSource::BufRead(byte_count) => {
+ self.bytes_consumed = byte_count;
+ let buf = try_io!(self.buf_read.fill_buf());
+ &buf[..byte_count]
+ }
+ BytesSource::Incomplete => {
+ self.incomplete.take_buffer()
+ }
+ };
+ match result {
+ Ok(()) => Some(Ok(unsafe { str::from_utf8_unchecked(bytes) })),
+ Err(()) => Some(Err(BufReadDecoderError::InvalidByteSequence(bytes))),
+ }
+ }
+}