Bug 1347224 - Part 1: Expose fallible methods on the rust ns[C]String bindings, r=froydnj
authorMichael Layzell <michael@thelayzells.com>
Mon, 20 Mar 2017 14:40:25 -0400
changeset 348446 367073fab1aa0cb09ab39782ec6c384e25e9a359
parent 348445 a697b074ae3715c4c774061d9ea8958a6ef24447
child 348447 c8862bd997502ac5ae8ec404917946b419b8dbae
push id88228
push usermichael@thelayzells.com
push dateMon, 20 Mar 2017 18:41:26 +0000
treeherdermozilla-inbound@c8862bd99750 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1347224
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1347224 - Part 1: Expose fallible methods on the rust ns[C]String bindings, r=froydnj This patch adds a series of fallible methods for the rust ns[C]String bindings, as well as the `set_length` method, which is the same as the `SetLength` method in C++. `set_length` is marked as unsafe. The decision was made to make the fallible methods seperate from the infallible methods, and to use seperate Rust->C++ bindings for each of them, rather than only binding the fallible bindings, and unwrapping them in rust-land. This is to try to match the C++ API as closely as possible, and to ensure that the behavior matches. MozReview-Commit-ID: FkSomkFUFGD
xpcom/rust/nsstring/src/lib.rs
xpcom/string/nsReadableUtils.cpp
xpcom/string/nsSubstring.cpp
--- a/xpcom/rust/nsstring/src/lib.rs
+++ b/xpcom/rust/nsstring/src/lib.rs
@@ -112,16 +112,17 @@
 //! can be useful when implementing FFI types which contain `ns[C]String` members
 //! which invoke their member's destructors through C++ code.
 
 #![allow(non_camel_case_types)]
 #![deny(warnings)]
 
 use std::ops::{Deref, DerefMut};
 use std::marker::PhantomData;
+use std::borrow;
 use std::slice;
 use std::ptr;
 use std::mem;
 use std::fmt;
 use std::cmp;
 use std::str;
 use std::u32;
 use std::os::raw::c_void;
@@ -145,21 +146,30 @@ const F_CLASS_FIXED: u32 = 1 << 16; // i
 
 ////////////////////////////////////
 // Generic String Bindings Macros //
 ////////////////////////////////////
 
 macro_rules! define_string_types {
     {
         char_t = $char_t: ty;
+
         AString = $AString: ident;
         String = $String: ident;
         FixedString = $FixedString: ident;
 
+        StringLike = $StringLike: ident;
+        StringAdapter = $StringAdapter: ident;
+
         StringRepr = $StringRepr: ident;
+
+        drop = $drop: ident;
+        assign = $assign: ident, $fallible_assign: ident;
+        append = $append: ident, $fallible_append: ident;
+        set_length = $set_length: ident, $fallible_set_length: ident;
     } => {
         /// The representation of a ns[C]String type in C++. This type is
         /// used internally by our definition of ns[C]String to ensure layout
         /// compatibility with the C++ ns[C]String type.
         ///
         /// This type may also be used in place of a C++ ns[C]String inside of
         /// struct definitions which are shared with C++, as it has identical
         /// layout to our ns[C]String type.
@@ -203,16 +213,82 @@ macro_rules! define_string_types {
         /// the construction by code outside of this module. It is used instead
         /// of a private `()` member because the `improper_ctypes` lint complains
         /// about some ZST members in `extern "C"` function declarations.
         #[repr(C)]
         pub struct $AString {
             _prohibit_constructor: [u8; 0],
         }
 
+        impl $AString {
+            /// Assign the value of `other` into self, overwriting any value
+            /// currently stored. Performs an optimized assignment when possible
+            /// if `other` is a `nsA[C]String`.
+            pub fn assign<T: $StringLike + ?Sized>(&mut self, other: &T) {
+                unsafe { $assign(self, other.adapt().as_ptr()) };
+            }
+
+            /// Assign the value of `other` into self, overwriting any value
+            /// currently stored. Performs an optimized assignment when possible
+            /// if `other` is a `nsA[C]String`.
+            ///
+            /// Returns Ok(()) on success, and Err(()) if the allocation failed.
+            pub fn fallible_assign<T: $StringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
+                if unsafe { $fallible_assign(self, other.adapt().as_ptr()) } {
+                    Ok(())
+                } else {
+                    Err(())
+                }
+            }
+
+            /// Append the value of `other` into self.
+            pub fn append<T: $StringLike + ?Sized>(&mut self, other: &T) {
+                unsafe { $append(self, other.adapt().as_ptr()) };
+            }
+
+            /// Append the value of `other` into self.
+            ///
+            /// Returns Ok(()) on success, and Err(()) if the allocation failed.
+            pub fn fallible_append<T: $StringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
+                if unsafe { $fallible_append(self, other.adapt().as_ptr()) } {
+                    Ok(())
+                } else {
+                    Err(())
+                }
+            }
+
+            /// Set the length of the string to the passed-in length, and expand
+            /// the backing capacity to match. This method is unsafe as it can
+            /// expose uninitialized memory when len is greater than the current
+            /// length of the string.
+            pub unsafe fn set_length(&mut self, len: u32) {
+                $set_length(self, len);
+            }
+
+            /// Set the length of the string to the passed-in length, and expand
+            /// the backing capacity to match. This method is unsafe as it can
+            /// expose uninitialized memory when len is greater than the current
+            /// length of the string.
+            ///
+            /// Returns Ok(()) on success, and Err(()) if the allocation failed.
+            pub unsafe fn fallible_set_length(&mut self, len: u32) -> Result<(), ()> {
+                if $fallible_set_length(self, len) {
+                    Ok(())
+                } else {
+                    Err(())
+                }
+            }
+
+            pub fn truncate(&mut self) {
+                unsafe {
+                    self.set_length(0);
+                }
+            }
+        }
+
         impl Deref for $AString {
             type Target = [$char_t];
             fn deref(&self) -> &[$char_t] {
                 unsafe {
                     // All $AString values point to a struct prefix which is
                     // identical to $StringRepr, this we can transmute `self`
                     // into $StringRepr to get the reference to the underlying
                     // data.
@@ -272,16 +348,24 @@ macro_rules! define_string_types {
                         length: 0,
                         flags: F_NONE,
                     },
                     _marker: PhantomData,
                 }
             }
         }
 
+        impl<'a> Drop for $String<'a> {
+            fn drop(&mut self) {
+                unsafe {
+                    $drop(&mut **self);
+                }
+            }
+        }
+
         impl<'a> Deref for $String<'a> {
             type Target = $AString;
             fn deref(&self) -> &$AString {
                 &self.hdr
             }
         }
 
         impl<'a> DerefMut for $String<'a> {
@@ -506,77 +590,156 @@ macro_rules! define_string_types {
             }
         }
 
         impl<'a, 'b> cmp::PartialEq<&'b str> for $FixedString<'a> {
             fn eq(&self, other: &&'b str) -> bool {
                 $AString::eq(self, *other)
             }
         }
+
+        /// An adapter type to allow for passing both types which coerce to
+        /// &[$char_type], and &$AString to a function, while still performing
+        /// optimized operations when passed the $AString.
+        pub enum $StringAdapter<'a> {
+            Borrowed($String<'a>),
+            Abstract(&'a $AString),
+        }
+
+        impl<'a> $StringAdapter<'a> {
+            fn as_ptr(&self) -> *const $AString {
+                &**self
+            }
+        }
+
+        impl<'a> Deref for $StringAdapter<'a> {
+            type Target = $AString;
+
+            fn deref(&self) -> &$AString {
+                match *self {
+                    $StringAdapter::Borrowed(ref s) => s,
+                    $StringAdapter::Abstract(ref s) => s,
+                }
+            }
+        }
+
+        /// This trait is implemented on types which are `ns[C]String`-like, in
+        /// that they can at very low cost be converted to a borrowed
+        /// `&nsA[C]String`. Unfortunately, the intermediate type
+        /// `ns[C]StringAdapter` is required as well due to types like `&[u8]`
+        /// needing to be (cheaply) wrapped in a `nsCString` on the stack to
+        /// create the `&nsACString`.
+        ///
+        /// This trait is used to DWIM when calling the methods on
+        /// `nsA[C]String`.
+        pub trait $StringLike {
+            fn adapt(&self) -> $StringAdapter;
+        }
+
+        impl<'a, T: $StringLike + ?Sized> $StringLike for &'a T {
+            fn adapt(&self) -> $StringAdapter {
+                <T as $StringLike>::adapt(*self)
+            }
+        }
+
+        impl<'a, T> $StringLike for borrow::Cow<'a, T>
+            where T: $StringLike + borrow::ToOwned + ?Sized {
+            fn adapt(&self) -> $StringAdapter {
+                <T as $StringLike>::adapt(self.as_ref())
+            }
+        }
+
+        impl $StringLike for $AString {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Abstract(self)
+            }
+        }
+
+        impl<'a> $StringLike for $String<'a> {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Abstract(self)
+            }
+        }
+
+        impl<'a> $StringLike for $FixedString<'a> {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Abstract(self)
+            }
+        }
+
+        impl $StringLike for [$char_t] {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Borrowed($String::from(self))
+            }
+        }
+
+        impl $StringLike for Vec<$char_t> {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Borrowed($String::from(&self[..]))
+            }
+        }
+
+        impl $StringLike for Box<[$char_t]> {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Borrowed($String::from(&self[..]))
+            }
+        }
     }
 }
 
 ///////////////////////////////////////////
 // Bindings for nsCString (u8 char type) //
 ///////////////////////////////////////////
 
 define_string_types! {
     char_t = u8;
 
     AString = nsACString;
     String = nsCString;
     FixedString = nsFixedCString;
 
+    StringLike = nsCStringLike;
+    StringAdapter = nsCStringAdapter;
+
     StringRepr = nsCStringRepr;
+
+    drop = Gecko_FinalizeCString;
+    assign = Gecko_AssignCString, Gecko_FallibleAssignCString;
+    append = Gecko_AppendCString, Gecko_FallibleAppendCString;
+    set_length = Gecko_SetLengthCString, Gecko_FallibleSetLengthCString;
 }
 
 impl nsACString {
-    pub fn assign<T: AsRef<[u8]> + ?Sized>(&mut self, other: &T) {
-        let s = nsCString::from(other.as_ref());
+    pub fn assign_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) {
+        self.truncate();
+        self.append_utf16(other);
+    }
+
+    pub fn fallible_assign_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
+        self.truncate();
+        self.fallible_append_utf16(other)
+    }
+
+    pub fn append_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) {
         unsafe {
-            Gecko_AssignCString(self, &*s);
+            Gecko_AppendUTF16toCString(self, other.adapt().as_ptr());
         }
     }
 
-    pub fn assign_utf16<T: AsRef<[u16]> + ?Sized>(&mut self, other: &T) {
-        self.assign(&[]);
-        self.append_utf16(other);
-    }
-
-    pub fn append<T: AsRef<[u8]> + ?Sized>(&mut self, other: &T) {
-        let s = nsCString::from(other.as_ref());
-        unsafe {
-            Gecko_AppendCString(self, &*s);
-        }
-    }
-
-    pub fn append_utf16<T: AsRef<[u16]> + ?Sized>(&mut self, other: &T) {
-        let s = nsString::from(other.as_ref());
-        unsafe {
-            Gecko_AppendUTF16toCString(self, &*s);
+    pub fn fallible_append_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
+        if unsafe { Gecko_FallibleAppendUTF16toCString(self, other.adapt().as_ptr()) } {
+            Ok(())
+        } else {
+            Err(())
         }
     }
 
     pub unsafe fn as_str_unchecked(&self) -> &str {
         str::from_utf8_unchecked(self)
     }
-
-    pub fn truncate(&mut self) {
-        unsafe {
-            Gecko_TruncateCString(self);
-        }
-    }
-}
-
-impl<'a> Drop for nsCString<'a> {
-    fn drop(&mut self) {
-        unsafe {
-            Gecko_FinalizeCString(&mut **self);
-        }
-    }
 }
 
 impl<'a> From<&'a str> for nsCString<'a> {
     fn from(s: &'a str) -> nsCString<'a> {
         s.as_bytes().into()
     }
 }
 
@@ -613,16 +776,34 @@ impl fmt::Debug for nsACString {
 }
 
 impl cmp::PartialEq<str> for nsACString {
     fn eq(&self, other: &str) -> bool {
         &self[..] == other.as_bytes()
     }
 }
 
+impl nsCStringLike for str {
+    fn adapt(&self) -> nsCStringAdapter {
+        nsCStringAdapter::Borrowed(nsCString::from(self))
+    }
+}
+
+impl nsCStringLike for String {
+    fn adapt(&self) -> nsCStringAdapter {
+        nsCStringAdapter::Borrowed(nsCString::from(&self[..]))
+    }
+}
+
+impl nsCStringLike for Box<str> {
+    fn adapt(&self) -> nsCStringAdapter {
+        nsCStringAdapter::Borrowed(nsCString::from(&self[..]))
+    }
+}
+
 #[macro_export]
 macro_rules! ns_auto_cstring {
     ($name:ident) => {
         let mut buf: [u8; 64] = [0; 64];
         let mut $name = $crate::nsFixedCString::new(&mut buf);
     }
 }
 
@@ -632,57 +813,49 @@ macro_rules! ns_auto_cstring {
 
 define_string_types! {
     char_t = u16;
 
     AString = nsAString;
     String = nsString;
     FixedString = nsFixedString;
 
+    StringLike = nsStringLike;
+    StringAdapter = nsStringAdapter;
+
     StringRepr = nsStringRepr;
+
+    drop = Gecko_FinalizeString;
+    assign = Gecko_AssignString, Gecko_FallibleAssignString;
+    append = Gecko_AppendString, Gecko_FallibleAppendString;
+    set_length = Gecko_SetLengthString, Gecko_FallibleSetLengthString;
 }
 
 impl nsAString {
-    pub fn assign<T: AsRef<[u16]> + ?Sized>(&mut self, other: &T) {
-        let s = nsString::from(other.as_ref());
-        unsafe {
-            Gecko_AssignString(self, &*s);
-        }
-    }
-
-    pub fn assign_utf8<T: AsRef<[u8]> + ?Sized>(&mut self, other: &T) {
-        self.assign(&[]);
+    pub fn assign_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) {
+        self.truncate();
         self.append_utf8(other);
     }
 
-    pub fn append<T: AsRef<[u16]> + ?Sized>(&mut self, other: &T) {
-        let s = nsString::from(other.as_ref());
+    pub fn fallible_assign_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
+        self.truncate();
+        self.fallible_append_utf8(other)
+    }
+
+    pub fn append_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) {
         unsafe {
-            Gecko_AppendString(self, &*s);
+            Gecko_AppendUTF8toString(self, other.adapt().as_ptr());
         }
     }
 
-    pub fn append_utf8<T: AsRef<[u8]> + ?Sized>(&mut self, other: &T) {
-        let s = nsCString::from(other.as_ref());
-        unsafe {
-            Gecko_AppendUTF8toString(self, &*s);
-        }
-    }
-
-    pub fn truncate(&mut self) {
-        unsafe {
-            Gecko_TruncateString(self);
-        }
-    }
-}
-
-impl<'a> Drop for nsString<'a> {
-    fn drop(&mut self) {
-        unsafe {
-            Gecko_FinalizeString(&mut **self);
+    pub fn fallible_append_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
+        if unsafe { Gecko_FallibleAppendUTF8toString(self, other.adapt().as_ptr()) } {
+            Ok(())
+        } else {
+            Err(())
         }
     }
 }
 
 // NOTE: The From impl for a string slice for nsString produces a <'static>
 // lifetime, as it allocates.
 impl<'a> From<&'a str> for nsString<'static> {
     fn from(s: &'a str) -> nsString<'static> {
@@ -725,36 +898,44 @@ macro_rules! ns_auto_string {
         let mut $name = $crate::nsFixedString::new(&mut buf);
     }
 }
 
 #[cfg(not(debug_assertions))]
 #[allow(non_snake_case)]
 unsafe fn Gecko_IncrementStringAdoptCount(_: *mut c_void) {}
 
-// NOTE: These bindings currently only expose infallible operations. Perhaps
-// consider allowing for fallible methods?
 extern "C" {
     #[cfg(debug_assertions)]
     fn Gecko_IncrementStringAdoptCount(data: *mut c_void);
 
     // Gecko implementation in nsSubstring.cpp
     fn Gecko_FinalizeCString(this: *mut nsACString);
+
     fn Gecko_AssignCString(this: *mut nsACString, other: *const nsACString);
     fn Gecko_AppendCString(this: *mut nsACString, other: *const nsACString);
-    fn Gecko_TruncateCString(this: *mut nsACString);
+    fn Gecko_SetLengthCString(this: *mut nsACString, length: u32);
+    fn Gecko_FallibleAssignCString(this: *mut nsACString, other: *const nsACString) -> bool;
+    fn Gecko_FallibleAppendCString(this: *mut nsACString, other: *const nsACString) -> bool;
+    fn Gecko_FallibleSetLengthCString(this: *mut nsACString, length: u32) -> bool;
 
     fn Gecko_FinalizeString(this: *mut nsAString);
+
     fn Gecko_AssignString(this: *mut nsAString, other: *const nsAString);
     fn Gecko_AppendString(this: *mut nsAString, other: *const nsAString);
-    fn Gecko_TruncateString(this: *mut nsAString);
+    fn Gecko_SetLengthString(this: *mut nsAString, length: u32);
+    fn Gecko_FallibleAssignString(this: *mut nsAString, other: *const nsAString) -> bool;
+    fn Gecko_FallibleAppendString(this: *mut nsAString, other: *const nsAString) -> bool;
+    fn Gecko_FallibleSetLengthString(this: *mut nsAString, length: u32) -> bool;
 
     // Gecko implementation in nsReadableUtils.cpp
     fn Gecko_AppendUTF16toCString(this: *mut nsACString, other: *const nsAString);
     fn Gecko_AppendUTF8toString(this: *mut nsAString, other: *const nsACString);
+    fn Gecko_FallibleAppendUTF16toCString(this: *mut nsACString, other: *const nsAString) -> bool;
+    fn Gecko_FallibleAppendUTF8toString(this: *mut nsAString, other: *const nsACString) -> bool;
 }
 
 //////////////////////////////////////
 // Repr Validation Helper Functions //
 //////////////////////////////////////
 
 pub mod test_helpers {
     //! This module only exists to help with ensuring that the layout of the
--- a/xpcom/string/nsReadableUtils.cpp
+++ b/xpcom/string/nsReadableUtils.cpp
@@ -1375,9 +1375,19 @@ void Gecko_AppendUTF16toCString(nsACStri
   AppendUTF16toUTF8(*aOther, *aThis);
 }
 
 void Gecko_AppendUTF8toString(nsAString* aThis, const nsACString* aOther)
 {
   AppendUTF8toUTF16(*aOther, *aThis);
 }
 
+bool Gecko_FallibleAppendUTF16toCString(nsACString* aThis, const nsAString* aOther)
+{
+  return AppendUTF16toUTF8(*aOther, *aThis, mozilla::fallible);
 }
+
+bool Gecko_FallibleAppendUTF8toString(nsAString* aThis, const nsACString* aOther)
+{
+  return AppendUTF8toUTF16(*aOther, *aThis, mozilla::fallible);
+}
+
+}
--- a/xpcom/string/nsSubstring.cpp
+++ b/xpcom/string/nsSubstring.cpp
@@ -365,19 +365,34 @@ void Gecko_AssignCString(nsACString* aTh
   aThis->Assign(*aOther);
 }
 
 void Gecko_AppendCString(nsACString* aThis, const nsACString* aOther)
 {
   aThis->Append(*aOther);
 }
 
-void Gecko_TruncateCString(nsACString* aThis)
+void Gecko_SetLengthCString(nsACString* aThis, uint32_t aLength)
+{
+  aThis->SetLength(aLength);
+}
+
+bool Gecko_FallibleAssignCString(nsACString* aThis, const nsACString* aOther)
 {
-  aThis->Truncate();
+  return aThis->Assign(*aOther, mozilla::fallible);
+}
+
+bool Gecko_FallibleAppendCString(nsACString* aThis, const nsACString* aOther)
+{
+  return aThis->Append(*aOther, mozilla::fallible);
+}
+
+bool Gecko_FallibleSetLengthCString(nsACString* aThis, uint32_t aLength)
+{
+  return aThis->SetLength(aLength, mozilla::fallible);
 }
 
 void Gecko_FinalizeString(nsAString* aThis)
 {
   aThis->~nsAString();
 }
 
 void Gecko_AssignString(nsAString* aThis, const nsAString* aOther)
@@ -385,14 +400,29 @@ void Gecko_AssignString(nsAString* aThis
   aThis->Assign(*aOther);
 }
 
 void Gecko_AppendString(nsAString* aThis, const nsAString* aOther)
 {
   aThis->Append(*aOther);
 }
 
-void Gecko_TruncateString(nsAString* aThis)
+void Gecko_SetLengthString(nsAString* aThis, uint32_t aLength)
+{
+  aThis->SetLength(aLength);
+}
+
+bool Gecko_FallibleAssignString(nsAString* aThis, const nsAString* aOther)
 {
-  aThis->Truncate();
+  return aThis->Assign(*aOther, mozilla::fallible);
+}
+
+bool Gecko_FallibleAppendString(nsAString* aThis, const nsAString* aOther)
+{
+  return aThis->Append(*aOther, mozilla::fallible);
+}
+
+bool Gecko_FallibleSetLengthString(nsAString* aThis, uint32_t aLength)
+{
+  return aThis->SetLength(aLength, mozilla::fallible);
 }
 
 } // extern "C"