/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
usecrate::{
nsACString, nsAString, nsCStringLike, BulkWriteOk, Gecko_FallibleAssignCString,
Latin1StringLike,
}; use encoding_rs::mem::*; use encoding_rs::Encoding; use std::slice;
/// Required math stated in the docs of /// `convert_utf16_to_utf8()`. #[inline(always)] fn times_three(a: usize) -> Option<usize> {
a.checked_mul(3)
}
/// Returns true if the string is both longer than a cache line /// and the first cache line is ASCII. #[inline(always)] fn long_string_starts_with_ascii(buffer: &[u8]) -> bool { // We examine data only up to the end of the cache line // to make this check minimally disruptive. if buffer.len() <= CACHE_LINE { returnfalse;
} let bound = CACHE_LINE - ((buffer.as_ptr() as usize) & CACHE_LINE_MASK);
is_ascii(&buffer[..bound])
}
/// Returns true if the string is both longer than two cache lines /// and the first two cache lines are Basic Latin. #[inline(always)] fn long_string_stars_with_basic_latin(buffer: &[u16]) -> bool { // We look at two cache lines with code unit size of two. There is need // to look at more than one cache line in the UTF-16 case, because looking // at just one cache line wouldn't catch non-ASCII Latin with high enough // probability with Latin-script languages that have relatively infrequent // non-ASCII characters. if buffer.len() <= CACHE_LINE { returnfalse;
} let bound = (CACHE_LINE * 2 - ((buffer.as_ptr() as usize) & CACHE_LINE_MASK)) / 2;
is_basic_latin(&buffer[..bound])
}
// Ignoring the copy avoidance complications of conversions between Latin1 and // UTF-8, a conversion function has the outward form of // `fn F(&mut self, other: &[T], old_len: usize) -> Result<BulkWriteOk, ()>`, // where `T` is either `u8` or `u16`. `other` is the slice whose converted // content are to be appended to `self` and `old_len` indicates how many // code unit of `self` are to be preserved (0 for the assignment case and // `self.len()` for the appending case). // // As implementation parameters a conversion function needs to know the // math for computing the worst case conversion length in code units given // the input length in code units. For a _constant conversion_ the number // of code units the conversion produces equals the number of code units // in the input. For a _shinking conversion_ the maximum number of code // units the conversion can produce equals the number of code units in // the input, but the conversion can produce fewer code units. Still, due // to implementation details, the function might want _one_ unit more of // output space. For an _expanding conversion_ (no need for macro), the // minimum number of code units produced by the conversion is the number // of code units in the input, but the conversion can produce more. // // Copy avoidance conversions avoid copying a refcounted buffer when it's // ASCII-only. // // Internally, a conversion function needs to know the underlying // encoding_rs conversion function, the math for computing the required // output buffer size and, depending on the case, the underlying // encoding_rs ASCII prefix handling function.
/// A conversion where the number of code units in the output is potentially /// smaller than the number of code units in the input. /// /// Takes the name of the method to be generated, the name of the conversion /// function and the type of the input slice. /// /// `$name` is the name of the function to generate /// `$convert` is the underlying `encoding_rs::mem` function to use /// `$other_ty` is the type of the input slice /// `$math` is the worst-case length math that `$convert` expects
macro_rules! shrinking_conversion {
(name = $name:ident,
convert = $convert:ident,
other_ty = $other_ty:ty,
math = $math:ident) => { fn $name(&mutself, other: $other_ty, old_len: usize) -> Result<BulkWriteOk, ()> { let needed = $math(other.len()).ok_or(())?; letmut handle = unsafe { self.bulk_write(old_len.checked_add(needed).ok_or(())?, old_len, false)? }; let written = $convert(other, &mut handle.as_mut_slice()[old_len..]); let new_len = old_len + written;
Ok(handle.finish(new_len, new_len > CACHE_LINE))
}
};
}
/// A conversion where the number of code units in the output is always equal /// to the number of code units in the input. /// /// Takes the name of the method to be generated, the name of the conversion /// function and the type of the input slice. /// /// `$name` is the name of the function to generate /// `$convert` is the underlying `encoding_rs::mem` function to use /// `$other_ty` is the type of the input slice
macro_rules! constant_conversion {
(name = $name:ident,
convert = $convert:ident,
other_ty = $other_ty:ty) => { fn $name(
&mutself,
other: $other_ty,
old_len: usize,
allow_shrinking: bool,
) -> Result<BulkWriteOk, ()> { let new_len = old_len.checked_add(other.len()).ok_or(())?; letmut handle = unsafe { self.bulk_write(new_len, old_len, allow_shrinking)? };
$convert(other, &mut handle.as_mut_slice()[old_len..]);
Ok(handle.finish(new_len, false))
}
};
}
/// An intermediate check for avoiding a copy and having an `StringBuffer` refcount increment /// instead when both `self` and `other` are `nsACString`s, `other` is entirely ASCII and all old /// data in `self` is discarded. /// /// `$name` is the name of the function to generate /// `$impl` is the underlying conversion that takes a slice and that is used /// when we can't just adopt the incoming buffer as-is /// `$string_like` is the kind of input taken
macro_rules! ascii_copy_avoidance {
(name = $name:ident,
implementation = $implementation:ident,
string_like = $string_like:ident) => { fn $name<T: $string_like + ?Sized>(
&mutself,
other: &T,
old_len: usize,
) -> Result<BulkWriteOk, ()> { let adapter = other.adapt(); let other_slice = adapter.as_ref(); let num_ascii = if adapter.is_abstract() && old_len == 0 { let up_to = Encoding::ascii_valid_up_to(other_slice); if up_to == other_slice.len() { // Calling something whose argument can be obtained from // the adapter rather than an nsStringLike avoids a huge // lifetime mess by keeping nsStringLike and // Latin1StringLike free of lifetime interdependencies. ifunsafe { Gecko_FallibleAssignCString(self, other.adapt().as_ptr()) } { return Ok(BulkWriteOk {});
} else { return Err(());
}
}
Some(up_to)
} else {
None
}; self.$implementation(other_slice, old_len, num_ascii)
}
};
}
impl nsAString { // Valid UTF-8 to UTF-16
// Documentation says the destination buffer needs to have // as many code units as the input.
shrinking_conversion!(
name = fallible_append_str_impl,
convert = convert_str_to_utf16,
other_ty = &str,
math = identity
);
/// Convert a valid UTF-8 string into valid UTF-16 and replace the content /// of this string with the conversion result. pubfn assign_str(&mutself, other: &str) { self.fallible_append_str_impl(other, 0)
.expect("Out of memory");
}
/// Convert a valid UTF-8 string into valid UTF-16 and fallibly replace the /// content of this string with the conversion result. pubfn fallible_assign_str(&mutself, other: &str) -> Result<(), ()> { self.fallible_append_str_impl(other, 0).map(|_| ())
}
/// Convert a valid UTF-8 string into valid UTF-16 and append the conversion /// to this string. pubfn append_str(&mutself, other: &str) { let len = self.len(); self.fallible_append_str_impl(other, len)
.expect("Out of memory");
}
/// Convert a valid UTF-8 string into valid UTF-16 and fallibly append the /// conversion to this string. pubfn fallible_append_str(&mutself, other: &str) -> Result<(), ()> { let len = self.len(); self.fallible_append_str_impl(other, len).map(|_| ())
}
// Potentially-invalid UTF-8 to UTF-16
// Documentation says the destination buffer needs to have // one more code unit than the input.
shrinking_conversion!(
name = fallible_append_utf8_impl,
convert = convert_utf8_to_utf16,
other_ty = &[u8],
math = plus_one
);
/// Convert a potentially-invalid UTF-8 string into valid UTF-16 /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and /// replace the content of this string with the conversion result. pubfn assign_utf8(&mutself, other: &[u8]) { self.fallible_append_utf8_impl(other, 0)
.expect("Out of memory");
}
/// Convert a potentially-invalid UTF-8 string into valid UTF-16 /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and /// fallibly replace the content of this string with the conversion result. pubfn fallible_assign_utf8(&mutself, other: &[u8]) -> Result<(), ()> { self.fallible_append_utf8_impl(other, 0).map(|_| ())
}
/// Convert a potentially-invalid UTF-8 string into valid UTF-16 /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and /// append the conversion result to this string. pubfn append_utf8(&mutself, other: &[u8]) { let len = self.len(); self.fallible_append_utf8_impl(other, len)
.expect("Out of memory");
}
/// Convert a potentially-invalid UTF-8 string into valid UTF-16 /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and /// fallibly append the conversion result to this string. pubfn fallible_append_utf8(&mutself, other: &[u8]) -> Result<(), ()> { let len = self.len(); self.fallible_append_utf8_impl(other, len).map(|_| ())
}
/// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!) /// into UTF-16 and replace the content of this string with the conversion result. pubfn assign_latin1(&mutself, other: &[u8]) { self.fallible_append_latin1_impl(other, 0, true)
.expect("Out of memory");
}
/// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!) /// into UTF-16 and fallibly replace the content of this string with the /// conversion result. pubfn fallible_assign_latin1(&mutself, other: &[u8]) -> Result<(), ()> { self.fallible_append_latin1_impl(other, 0, true).map(|_| ())
}
/// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!) /// into UTF-16 and append the conversion result to this string. pubfn append_latin1(&mutself, other: &[u8]) { let len = self.len(); self.fallible_append_latin1_impl(other, len, false)
.expect("Out of memory");
}
/// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!) /// into UTF-16 and fallibly append the conversion result to this string. pubfn fallible_append_latin1(&mutself, other: &[u8]) -> Result<(), ()> { let len = self.len(); self.fallible_append_latin1_impl(other, len, false)
.map(|_| ())
}
}
impl nsACString { // UTF-16 to UTF-8
fn fallible_append_utf16_to_utf8_impl(
&mutself,
other: &[u16],
old_len: usize,
) -> Result<BulkWriteOk, ()> { // We first size the buffer for ASCII if the first two cache lines are ASCII. If that turns out // not to be enough, we size for the worst case given the length of the remaining input at that // point. BUT if the worst case fits inside the inline capacity of an autostring, we skip // the ASCII stuff. let worst_case_needed = iflet Some(inline_capacity) = self.inline_capacity() { let worst_case = times_three(other.len()).ok_or(())?; if worst_case <= inline_capacity {
Some(worst_case)
} else {
None
}
} else {
None
}; let (filled, read, mut handle) = if worst_case_needed.is_none() && long_string_stars_with_basic_latin(other) { let new_len_with_ascii = old_len.checked_add(other.len()).ok_or(())?; letmut handle = unsafe { self.bulk_write(new_len_with_ascii, old_len, false)? }; let (read, written) =
convert_utf16_to_utf8_partial(other, &mut handle.as_mut_slice()[old_len..]); let left = other.len() - read; if left == 0 { return Ok(handle.finish(old_len + written, true));
} let filled = old_len + written; let needed = times_three(left).ok_or(())?; let new_len = filled.checked_add(needed).ok_or(())?; unsafe {
handle.restart_bulk_write(new_len, filled, false)?;
}
(filled, read, handle)
} else { // Started with non-ASCII. Compute worst case let needed = iflet Some(n) = worst_case_needed {
n
} else {
times_three(other.len()).ok_or(())?
}; let new_len = old_len.checked_add(needed).ok_or(())?; let handle = unsafe { self.bulk_write(new_len, old_len, false)? };
(old_len, 0, handle)
}; let written = convert_utf16_to_utf8(&other[read..], &mut handle.as_mut_slice()[filled..]);
Ok(handle.finish(filled + written, true))
}
/// Convert a potentially-invalid UTF-16 string into valid UTF-8 /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and /// replace the content of this string with the conversion result. pubfn assign_utf16_to_utf8(&mutself, other: &[u16]) { self.fallible_append_utf16_to_utf8_impl(other, 0)
.expect("Out of memory");
}
/// Convert a potentially-invalid UTF-16 string into valid UTF-8 /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and /// fallibly replace the content of this string with the conversion result. pubfn fallible_assign_utf16_to_utf8(&mutself, other: &[u16]) -> Result<(), ()> { self.fallible_append_utf16_to_utf8_impl(other, 0)
.map(|_| ())
}
/// Convert a potentially-invalid UTF-16 string into valid UTF-8 /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and /// append the conversion result to this string. pubfn append_utf16_to_utf8(&mutself, other: &[u16]) { let len = self.len(); self.fallible_append_utf16_to_utf8_impl(other, len)
.expect("Out of memory");
}
/// Convert a potentially-invalid UTF-16 string into valid UTF-8 /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and /// fallibly append the conversion result to this string. pubfn fallible_append_utf16_to_utf8(&mutself, other: &[u16]) -> Result<(), ()> { let len = self.len(); self.fallible_append_utf16_to_utf8_impl(other, len)
.map(|_| ())
}
/// Convert a UTF-16 string whose all code points are below U+0100 into /// a Latin1 (scalar value is byte value; not windows-1252!) string and /// replace the content of this string with the conversion result. /// /// # Panics /// /// If the input contains code points above U+00FF or is not valid UTF-16, /// panics in debug mode and produces garbage in a memory-safe way in /// release builds. The nature of the garbage may differ based on CPU /// architecture and must not be relied upon. pubfn assign_utf16_to_latin1_lossy(&mutself, other: &[u16]) { self.fallible_append_utf16_to_latin1_lossy_impl(other, 0, true)
.expect("Out of memory");
}
/// Convert a UTF-16 string whose all code points are below U+0100 into /// a Latin1 (scalar value is byte value; not windows-1252!) string and /// fallibly replace the content of this string with the conversion result. /// /// # Panics /// /// If the input contains code points above U+00FF or is not valid UTF-16, /// panics in debug mode and produces garbage in a memory-safe way in /// release builds. The nature of the garbage may differ based on CPU /// architecture and must not be relied upon. pubfn fallible_assign_utf16_to_latin1_lossy(&mutself, other: &[u16]) -> Result<(), ()> { self.fallible_append_utf16_to_latin1_lossy_impl(other, 0, true)
.map(|_| ())
}
/// Convert a UTF-16 string whose all code points are below U+0100 into /// a Latin1 (scalar value is byte value; not windows-1252!) string and /// append the conversion result to this string. /// /// # Panics /// /// If the input contains code points above U+00FF or is not valid UTF-16, /// panics in debug mode and produces garbage in a memory-safe way in /// release builds. The nature of the garbage may differ based on CPU /// architecture and must not be relied upon. pubfn append_utf16_to_latin1_lossy(&mutself, other: &[u16]) { let len = self.len(); self.fallible_append_utf16_to_latin1_lossy_impl(other, len, false)
.expect("Out of memory");
}
/// Convert a UTF-16 string whose all code points are below U+0100 into /// a Latin1 (scalar value is byte value; not windows-1252!) string and /// fallibly append the conversion result to this string. /// /// # Panics /// /// If the input contains code points above U+00FF or is not valid UTF-16, /// panics in debug mode and produces garbage in a memory-safe way in /// release builds. The nature of the garbage may differ based on CPU /// architecture and must not be relied upon. pubfn fallible_append_utf16_to_latin1_lossy(&mutself, other: &[u16]) -> Result<(), ()> { let len = self.len(); self.fallible_append_utf16_to_latin1_lossy_impl(other, len, false)
.map(|_| ())
}
fn fallible_append_utf8_to_latin1_lossy_impl(
&mutself,
other: &[u8],
old_len: usize,
maybe_num_ascii: Option<usize>,
) -> Result<BulkWriteOk, ()> { let new_len = old_len.checked_add(other.len()).ok_or(())?; let num_ascii = maybe_num_ascii.unwrap_or(0); // Already checked for overflow above, so this can't overflow. let old_len_plus_num_ascii = old_len + num_ascii; letmut handle = unsafe { self.bulk_write(new_len, old_len, false)? }; let written = { let buffer = handle.as_mut_slice(); if num_ascii != 0 {
(&mut buffer[old_len..old_len_plus_num_ascii]).copy_from_slice(&other[..num_ascii]);
}
convert_utf8_to_latin1_lossy(&other[num_ascii..], &mut buffer[old_len_plus_num_ascii..])
};
Ok(handle.finish(old_len_plus_num_ascii + written, true))
}
/// Convert a UTF-8 string whose all code points are below U+0100 into /// a Latin1 (scalar value is byte value; not windows-1252!) string and /// replace the content of this string with the conversion result. /// /// # Panics /// /// If the input contains code points above U+00FF or is not valid UTF-8, /// panics in debug mode and produces garbage in a memory-safe way in /// release builds. The nature of the garbage may differ based on CPU /// architecture and must not be relied upon. pubfn assign_utf8_to_latin1_lossy<T: nsCStringLike + ?Sized>(&mutself, other: &T) { self.fallible_append_utf8_to_latin1_lossy_check(other, 0)
.expect("Out of memory");
}
/// Convert a UTF-8 string whose all code points are below U+0100 into /// a Latin1 (scalar value is byte value; not windows-1252!) string and /// fallibly replace the content of this string with the conversion result. /// /// # Panics /// /// If the input contains code points above U+00FF or is not valid UTF-8, /// panics in debug mode and produces garbage in a memory-safe way in /// release builds. The nature of the garbage may differ based on CPU /// architecture and must not be relied upon. pubfn fallible_assign_utf8_to_latin1_lossy<T: nsCStringLike + ?Sized>(
&mutself,
other: &T,
) -> Result<(), ()> { self.fallible_append_utf8_to_latin1_lossy_check(other, 0)
.map(|_| ())
}
/// Convert a UTF-8 string whose all code points are below U+0100 into /// a Latin1 (scalar value is byte value; not windows-1252!) string and /// append the conversion result to this string. /// /// # Panics /// /// If the input contains code points above U+00FF or is not valid UTF-8, /// panics in debug mode and produces garbage in a memory-safe way in /// release builds. The nature of the garbage may differ based on CPU /// architecture and must not be relied upon. pubfn append_utf8_to_latin1_lossy<T: nsCStringLike + ?Sized>(&mutself, other: &T) { let len = self.len(); self.fallible_append_utf8_to_latin1_lossy_check(other, len)
.expect("Out of memory");
}
/// Convert a UTF-8 string whose all code points are below U+0100 into /// a Latin1 (scalar value is byte value; not windows-1252!) string and /// fallibly append the conversion result to this string. /// /// # Panics /// /// If the input contains code points above U+00FF or is not valid UTF-8, /// panics in debug mode and produces garbage in a memory-safe way in /// release builds. The nature of the garbage may differ based on CPU /// architecture and must not be relied upon. pubfn fallible_append_utf8_to_latin1_lossy<T: nsCStringLike + ?Sized>(
&mutself,
other: &T,
) -> Result<(), ()> { let len = self.len(); self.fallible_append_utf8_to_latin1_lossy_check(other, len)
.map(|_| ())
}
fn fallible_append_latin1_to_utf8_impl(
&mutself,
other: &[u8],
old_len: usize,
maybe_num_ascii: Option<usize>,
) -> Result<BulkWriteOk, ()> { let (filled, read, mut handle) = iflet Some(num_ascii) = maybe_num_ascii { // Wrapper checked for ASCII let left = other.len() - num_ascii; let filled = old_len + num_ascii; let needed = left.checked_mul(2).ok_or(())?; let new_len = filled.checked_add(needed).ok_or(())?; letmut handle = unsafe { self.bulk_write(new_len, old_len, false)? }; if num_ascii != 0 {
(&mut handle.as_mut_slice()[old_len..filled]).copy_from_slice(&other[..num_ascii]);
}
(filled, num_ascii, handle)
} else { let worst_case_needed = iflet Some(inline_capacity) = self.inline_capacity() { let worst_case = other.len().checked_mul(2).ok_or(())?; if worst_case <= inline_capacity {
Some(worst_case)
} else {
None
}
} else {
None
}; if worst_case_needed.is_none() && long_string_starts_with_ascii(other) { // Wrapper didn't check for ASCII, so let's see if `other` starts with ASCII // `other` starts with ASCII, so let's first size the buffer // with optimism that it's ASCII-only. let new_len_with_ascii = old_len.checked_add(other.len()).ok_or(())?; letmut handle = unsafe { self.bulk_write(new_len_with_ascii, old_len, false)? }; let (read, written) =
convert_latin1_to_utf8_partial(other, &mut handle.as_mut_slice()[old_len..]); let left = other.len() - read; let filled = old_len + written; if left == 0 { // `other` fit in the initial allocation return Ok(handle.finish(filled, true));
} let needed = left.checked_mul(2).ok_or(())?; let new_len = filled.checked_add(needed).ok_or(())?; unsafe {
handle.restart_bulk_write(new_len, filled, false)?;
}
(filled, read, handle)
} else { // Started with non-ASCII. Assume worst case. let needed = iflet Some(n) = worst_case_needed {
n
} else {
other.len().checked_mul(2).ok_or(())?
}; let new_len = old_len.checked_add(needed).ok_or(())?; let handle = unsafe { self.bulk_write(new_len, old_len, false)? };
(old_len, 0, handle)
}
}; let written = convert_latin1_to_utf8(&other[read..], &mut handle.as_mut_slice()[filled..]);
Ok(handle.finish(filled + written, true))
}
/// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!) /// into UTF-8 and replace the content of this string with the conversion result. pubfn assign_latin1_to_utf8<T: Latin1StringLike + ?Sized>(&mutself, other: &T) { self.fallible_append_latin1_to_utf8_check(other, 0)
.expect("Out of memory");
}
/// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!) /// into UTF-8 and fallibly replace the content of this string with the /// conversion result. pubfn fallible_assign_latin1_to_utf8<T: Latin1StringLike + ?Sized>(
&mutself,
other: &T,
) -> Result<(), ()> { self.fallible_append_latin1_to_utf8_check(other, 0)
.map(|_| ())
}
/// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!) /// into UTF-8 and append the conversion result to this string. pubfn append_latin1_to_utf8<T: Latin1StringLike + ?Sized>(&mutself, other: &T) { let len = self.len(); self.fallible_append_latin1_to_utf8_check(other, len)
.expect("Out of memory");
}
/// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!) /// into UTF-8 and fallibly append the conversion result to this string. pubfn fallible_append_latin1_to_utf8<T: Latin1StringLike + ?Sized>(
&mutself,
other: &T,
) -> Result<(), ()> { let len = self.len(); self.fallible_append_latin1_to_utf8_check(other, len)
.map(|_| ())
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.