/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::{ 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 { a.checked_mul(3) } #[inline(always)] fn identity(a: usize) -> Option { Some(a) } #[inline(always)] fn plus_one(a: usize) -> Option { a.checked_add(1) } /// Typical cache line size per /// https://stackoverflow.com/questions/14707803/line-size-of-l1-and-l2-caches /// /// For consistent behavior, not trying to use 128 on aarch64 /// or other fanciness like that. const CACHE_LINE: usize = 64; const CACHE_LINE_MASK: usize = CACHE_LINE - 1; /// 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 { return false; } 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 { return false; } 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`, // 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(&mut self, other: $other_ty, old_len: usize) -> Result { let needed = $math(other.len()).ok_or(())?; let mut 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( &mut self, other: $other_ty, old_len: usize, allow_shrinking: bool, ) -> Result { let new_len = old_len.checked_add(other.len()).ok_or(())?; let mut 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 `nsStringBuffer` /// 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( &mut self, other: &T, old_len: usize, ) -> Result { 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. if unsafe { 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. pub fn assign_str(&mut self, 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. pub fn fallible_assign_str(&mut self, 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. pub fn append_str(&mut self, 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. pub fn fallible_append_str(&mut self, 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. pub fn assign_utf8(&mut self, 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. pub fn fallible_assign_utf8(&mut self, 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. pub fn append_utf8(&mut self, 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. pub fn fallible_append_utf8(&mut self, other: &[u8]) -> Result<(), ()> { let len = self.len(); self.fallible_append_utf8_impl(other, len).map(|_| ()) } // Latin1 to UTF-16 constant_conversion!( name = fallible_append_latin1_impl, convert = convert_latin1_to_utf16, other_ty = &[u8] ); /// 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. pub fn assign_latin1(&mut self, 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. pub fn fallible_assign_latin1(&mut self, 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. pub fn append_latin1(&mut self, 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. pub fn fallible_append_latin1(&mut self, 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( &mut self, other: &[u16], old_len: usize, ) -> Result { // 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 = if let 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(())?; let mut 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 = if let 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. pub fn assign_utf16_to_utf8(&mut self, 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. pub fn fallible_assign_utf16_to_utf8(&mut self, 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. pub fn append_utf16_to_utf8(&mut self, 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. pub fn fallible_append_utf16_to_utf8(&mut self, other: &[u16]) -> Result<(), ()> { let len = self.len(); self.fallible_append_utf16_to_utf8_impl(other, len) .map(|_| ()) } // UTF-16 to Latin1 constant_conversion!( name = fallible_append_utf16_to_latin1_lossy_impl, convert = convert_utf16_to_latin1_lossy, other_ty = &[u16] ); /// 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. pub fn assign_utf16_to_latin1_lossy(&mut self, 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. pub fn fallible_assign_utf16_to_latin1_lossy(&mut self, 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. pub fn append_utf16_to_latin1_lossy(&mut self, 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. pub fn fallible_append_utf16_to_latin1_lossy(&mut self, other: &[u16]) -> Result<(), ()> { let len = self.len(); self.fallible_append_utf16_to_latin1_lossy_impl(other, len, false) .map(|_| ()) } // UTF-8 to Latin1 ascii_copy_avoidance!( name = fallible_append_utf8_to_latin1_lossy_check, implementation = fallible_append_utf8_to_latin1_lossy_impl, string_like = nsCStringLike ); fn fallible_append_utf8_to_latin1_lossy_impl( &mut self, other: &[u8], old_len: usize, maybe_num_ascii: Option, ) -> Result { 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; let mut 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. pub fn assign_utf8_to_latin1_lossy(&mut self, 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. pub fn fallible_assign_utf8_to_latin1_lossy( &mut self, 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. pub fn append_utf8_to_latin1_lossy(&mut self, 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. pub fn fallible_append_utf8_to_latin1_lossy( &mut self, other: &T, ) -> Result<(), ()> { let len = self.len(); self.fallible_append_utf8_to_latin1_lossy_check(other, len) .map(|_| ()) } // Latin1 to UTF-8 CString ascii_copy_avoidance!( name = fallible_append_latin1_to_utf8_check, implementation = fallible_append_latin1_to_utf8_impl, string_like = Latin1StringLike ); fn fallible_append_latin1_to_utf8_impl( &mut self, other: &[u8], old_len: usize, maybe_num_ascii: Option, ) -> Result { let (filled, read, mut handle) = if let 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(())?; let mut 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 = if let 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(())?; let mut 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 = if let 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. pub fn assign_latin1_to_utf8(&mut self, 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. pub fn fallible_assign_latin1_to_utf8( &mut self, 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. pub fn append_latin1_to_utf8(&mut self, 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. pub fn fallible_append_latin1_to_utf8( &mut self, other: &T, ) -> Result<(), ()> { let len = self.len(); self.fallible_append_latin1_to_utf8_check(other, len) .map(|_| ()) } } #[no_mangle] pub unsafe extern "C" fn nsstring_fallible_append_utf8_impl( this: *mut nsAString, other: *const u8, other_len: usize, old_len: usize, ) -> bool { let other_slice = slice::from_raw_parts(other, other_len); (*this) .fallible_append_utf8_impl(other_slice, old_len) .is_ok() } #[no_mangle] pub unsafe extern "C" fn nsstring_fallible_append_latin1_impl( this: *mut nsAString, other: *const u8, other_len: usize, old_len: usize, allow_shrinking: bool, ) -> bool { let other_slice = slice::from_raw_parts(other, other_len); (*this) .fallible_append_latin1_impl(other_slice, old_len, allow_shrinking) .is_ok() } #[no_mangle] pub unsafe extern "C" fn nscstring_fallible_append_utf16_to_utf8_impl( this: *mut nsACString, other: *const u16, other_len: usize, old_len: usize, ) -> bool { let other_slice = slice::from_raw_parts(other, other_len); (*this) .fallible_append_utf16_to_utf8_impl(other_slice, old_len) .is_ok() } #[no_mangle] pub unsafe extern "C" fn nscstring_fallible_append_utf16_to_latin1_lossy_impl( this: *mut nsACString, other: *const u16, other_len: usize, old_len: usize, allow_shrinking: bool, ) -> bool { let other_slice = slice::from_raw_parts(other, other_len); (*this) .fallible_append_utf16_to_latin1_lossy_impl(other_slice, old_len, allow_shrinking) .is_ok() } #[no_mangle] pub unsafe extern "C" fn nscstring_fallible_append_utf8_to_latin1_lossy_check( this: *mut nsACString, other: *const nsACString, old_len: usize, ) -> bool { (*this) .fallible_append_utf8_to_latin1_lossy_check(&*other, old_len) .is_ok() } #[no_mangle] pub unsafe extern "C" fn nscstring_fallible_append_latin1_to_utf8_check( this: *mut nsACString, other: *const nsACString, old_len: usize, ) -> bool { (*this) .fallible_append_latin1_to_utf8_check(&*other, old_len) .is_ok() }