Skip to content

Commit 0a4d9a5

Browse files
COOP_PREFERRED for String and Cow of CStr etc.
1 parent 0537478 commit 0a4d9a5

File tree

2 files changed

+47
-29
lines changed

2 files changed

+47
-29
lines changed

library/alloc/src/ffi/c_str.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,7 @@ impl CStr {
11261126
where [(); core::alloc::co_alloc_metadata_num_slots_with_preference::<Global>(COOP_PREFERRED)]:,
11271127
{
11281128
// false = no need for co-alloc metadata, since it would get lost once converted to the slice.
1129-
String::<COOP_PREFERRED>::from_utf8_lossy(self.to_bytes())
1129+
String::<COOP_PREFERRED>::from_utf8_lossy_coop_or_not(self.to_bytes())
11301130
}
11311131

11321132
/// Converts a <code>[Box]<[CStr]></code> into a [`CString`] without copying or allocating.

library/alloc/src/string.rs

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,41 @@ where
594594
}
595595
}
596596

597+
/// Like `from_utf8_lossy`, but this honors COOP_PREFERRED.
598+
#[unstable(feature = "compile_check_struct_with_const_generic_added_associated_function", reason = "confirm_or_fix_the_function_name", issue = "none")]
599+
pub fn from_utf8_lossy_coop_or_not(v: &[u8]) -> Cow<'_, str, COOP_PREFERRED> {
600+
let mut iter = Utf8Chunks::new(v);
601+
602+
let first_valid = if let Some(chunk) = iter.next() {
603+
let valid = chunk.valid();
604+
if chunk.invalid().is_empty() {
605+
debug_assert_eq!(valid.len(), v.len());
606+
return Cow::Borrowed(valid);
607+
}
608+
valid
609+
} else {
610+
return Cow::Borrowed("");
611+
};
612+
613+
const REPLACEMENT: &str = "\u{FFFD}";
614+
615+
#[allow(unused_braces)]
616+
let mut res = String::<COOP_PREFERRED>::with_capacity(v.len());
617+
res.push_str(first_valid);
618+
res.push_str(REPLACEMENT);
619+
620+
for chunk in iter {
621+
res.push_str(chunk.valid());
622+
if !chunk.invalid().is_empty() {
623+
res.push_str(REPLACEMENT);
624+
}
625+
}
626+
627+
Cow::Owned(res)
628+
}
629+
}
630+
631+
impl String {
597632
/// Converts a slice of bytes to a string, including invalid characters.
598633
///
599634
/// Strings are made of bytes ([`u8`]), and a slice of bytes
@@ -643,39 +678,22 @@ where
643678
///
644679
/// assert_eq!("Hello �World", output);
645680
/// ```
681+
/// @FIXME Should this return Cow<'_, str, false> instead, so it's both
682+
/// - backwards compatible, and
683+
/// - independent of DEFAULT_COOP_PREFERRED (for backwards compatbility)? But `String` (and others) use default, too.
646684
#[must_use]
647685
#[cfg(not(no_global_oom_handling))]
648686
#[stable(feature = "rust1", since = "1.0.0")]
649-
pub fn from_utf8_lossy(v: &[u8]) -> Cow<'_, str, COOP_PREFERRED> {
650-
let mut iter = Utf8Chunks::new(v);
651-
652-
let first_valid = if let Some(chunk) = iter.next() {
653-
let valid = chunk.valid();
654-
if chunk.invalid().is_empty() {
655-
debug_assert_eq!(valid.len(), v.len());
656-
return Cow::Borrowed(valid);
657-
}
658-
valid
659-
} else {
660-
return Cow::Borrowed("");
661-
};
662-
663-
const REPLACEMENT: &str = "\u{FFFD}";
664-
665-
let mut res = String::<COOP_PREFERRED>::with_capacity(v.len());
666-
res.push_str(first_valid);
667-
res.push_str(REPLACEMENT);
668-
669-
for chunk in iter {
670-
res.push_str(chunk.valid());
671-
if !chunk.invalid().is_empty() {
672-
res.push_str(REPLACEMENT);
673-
}
674-
}
675-
676-
Cow::Owned(res)
687+
#[allow(unused_braces)]
688+
pub fn from_utf8_lossy(v: &[u8]) -> Cow<'_, str, {DEFAULT_COOP_PREFERRED!()}> {
689+
String::<{DEFAULT_COOP_PREFERRED!()}>::from_utf8_lossy_coop_or_not(v)
677690
}
691+
}
678692

693+
impl<const COOP_PREFERRED: bool> String<COOP_PREFERRED>
694+
where
695+
[(); core::alloc::co_alloc_metadata_num_slots_with_preference::<Global>(COOP_PREFERRED)]:,
696+
{
679697
/// Decode a UTF-16–encoded vector `v` into a `String`, returning [`Err`]
680698
/// if `v` contains any invalid data.
681699
///

0 commit comments

Comments
 (0)