zr/lossy_utf8.rs
1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// Wraps a binary string that may contain non-UTF-8 bytes for formatting.
6/// Invalid bytes are formatted as `\u{FFFFD}`.
7pub fn from_utf8_lossy(bytes: &[u8]) -> LossyUtf8<'_> {
8 LossyUtf8(bytes)
9}
10
11pub struct LossyUtf8<'a>(&'a [u8]);
12
13impl core::fmt::Display for LossyUtf8<'_> {
14 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
15 let mut bytes = self.0;
16 while !bytes.is_empty() {
17 match core::str::from_utf8(bytes) {
18 Ok(s) => {
19 f.write_str(s)?;
20 break;
21 }
22 Err(err) => {
23 let (valid, rest) = bytes.split_at(err.valid_up_to());
24 if !valid.is_empty() {
25 // SAFETY: `valid` was verified by `from_utf8`.
26 let s = unsafe { core::str::from_utf8_unchecked(valid) };
27 f.write_str(s)?;
28 }
29 f.write_str("\u{FFFD}")?;
30 match err.error_len() {
31 Some(len) => bytes = &rest[len..],
32 None => break,
33 }
34 }
35 }
36 }
37 Ok(())
38 }
39}