Skip to content

Commit 92ea11e

Browse files
committed
truncate long slices instead of panicking
1 parent 9afa762 commit 92ea11e

File tree

1 file changed

+35
-10
lines changed

1 file changed

+35
-10
lines changed

crates/bevy_utils/src/label.rs

+35-10
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,33 @@ pub struct StuffedStr<'a> {
6666
unsafe impl<'a> Send for StuffedStr<'a> {}
6767
unsafe impl<'a> Sync for StuffedStr<'a> {}
6868

69+
/// Truncates the passed string slice to be of length `len`, or shorter.
70+
#[cold]
71+
#[inline(never)]
72+
fn truncate(str: &str, mut len: usize) -> &str {
73+
// Just keep lowering the expected length until we hit a unicode boundary.
74+
// This is messy but if the string is long enough that we need to truncate it,
75+
// it's too long for anyone to notice what the end looks like.
76+
loop {
77+
if let Some(str) = str.get(..len) {
78+
break str;
79+
}
80+
// In practice this won't be able to underflow, since `str.get(..0)` will always succeed.
81+
len -= 1;
82+
}
83+
}
84+
6985
impl<'a> StuffedStr<'a> {
7086
const DATA_MASK: usize = !Self::LEN_MASK;
7187
const LEN_MASK: usize = !0 << 16;
7288

73-
pub fn new(str: &'a str, data: u16) -> Self {
74-
#[cold]
75-
#[inline(never)]
76-
fn panic() -> ! {
77-
// needs a better message but its late
78-
panic!("string exceeds {} bytes", StuffedStr::LEN_MASK >> 16);
79-
}
80-
81-
let ptr = str.as_ptr();
89+
pub fn new(mut str: &'a str, data: u16) -> Self {
8290
// Make sure there's enough room to store the data.
8391
if str.len().leading_zeros() < 16 {
84-
panic();
92+
str = truncate(str, Self::LEN_MASK >> 16);
93+
debug_assert!(str.len().leading_zeros() < 16);
8594
}
95+
let ptr = str.as_ptr();
8696
let meta = data as usize | str.len() << 16;
8797
Self {
8898
ptr,
@@ -202,3 +212,18 @@ macro_rules! define_label {
202212
}
203213
};
204214
}
215+
216+
#[cfg(test)]
217+
mod tests {
218+
use super::*;
219+
220+
#[test]
221+
fn test_truncate() {
222+
// Slice at byte '4'
223+
assert_eq!(truncate("Hello, World!", 4), "Hell");
224+
225+
// Slicing off at byte '3' would be inside of the emoji,
226+
// so instead the whole emoji gets sliced off.
227+
assert_eq!(truncate("x😂", 3), "x");
228+
}
229+
}

0 commit comments

Comments
 (0)