Bug 1359353 - Make the backing buffers of XPCOM strings available as mutable slices. r?Mystor. draft
authorHenri Sivonen <hsivonen@hsivonen.fi>
Tue, 25 Apr 2017 13:17:48 +0300
changeset 568858 b6a1a526c35668d018967222ed2ed85ee7b3a9d6
parent 567827 a81a4d55ba8b9dcaf62f73036fce605c58e09a6d
child 569370 0bec9babd19f3d15578feb01e6a5804a06598bf7
push id56004
push userbmo:hsivonen@hsivonen.fi
push dateWed, 26 Apr 2017 17:31:23 +0000
reviewersMystor
bugs1359353
milestone55.0a1
Bug 1359353 - Make the backing buffers of XPCOM strings available as mutable slices. r?Mystor. MozReview-Commit-ID: KeJ2Qb3R5ET
xpcom/rust/nsstring/gtest/Test.cpp
xpcom/rust/nsstring/gtest/test.rs
xpcom/rust/nsstring/src/lib.rs
xpcom/string/nsSubstring.cpp
--- a/xpcom/rust/nsstring/gtest/Test.cpp
+++ b/xpcom/rust/nsstring/gtest/Test.cpp
@@ -129,8 +129,29 @@ extern "C" void Rust_StringWrite();
 TEST(RustNsString, StringWrite) {
   Rust_StringWrite();
 }
 
 extern "C" void Rust_FromEmptyRustString();
 TEST(RustNsString, FromEmptyRustString) {
   Rust_FromEmptyRustString();
 }
+
+extern "C" void Rust_WriteToBufferFromRust(nsACString* aCStr, nsAString* aStr, nsACString* aFallibleCStr, nsAString* aFallibleStr);
+TEST(RustNsString, WriteToBufferFromRust) {
+  nsAutoCString cStr;
+  nsAutoString str;
+  nsAutoCString fallibleCStr;
+  nsAutoString fallibleStr;
+
+  cStr.AssignLiteral("abc");
+  str.AssignLiteral("abc");
+  fallibleCStr.AssignLiteral("abc");
+  fallibleStr.AssignLiteral("abc");
+
+  Rust_WriteToBufferFromRust(&cStr, &str, &fallibleCStr, &fallibleStr);
+
+  EXPECT_TRUE(cStr.EqualsASCII("ABC"));
+  EXPECT_TRUE(str.EqualsASCII("ABC"));
+  EXPECT_TRUE(fallibleCStr.EqualsASCII("ABC"));
+  EXPECT_TRUE(fallibleStr.EqualsASCII("ABC"));
+}
+
--- a/xpcom/rust/nsstring/gtest/test.rs
+++ b/xpcom/rust/nsstring/gtest/test.rs
@@ -111,8 +111,31 @@ pub extern fn Rust_StringWrite() {
 }
 
 #[no_mangle]
 pub extern fn Rust_FromEmptyRustString() {
     let mut test = nsString::from("Blah");
     test.assign_utf8(&nsCString::from(String::new()));
     assert!(test.is_empty());
 }
+
+#[no_mangle]
+pub extern fn Rust_WriteToBufferFromRust(cs: *mut nsACString, s: *mut nsAString, fallible_cs: *mut nsACString, fallible_s: *mut nsAString) {
+    unsafe {
+        let cs_buf = (*cs).to_mut();
+        let s_buf = (*s).to_mut();
+        let fallible_cs_buf = (*fallible_cs).fallible_to_mut().unwrap();
+        let fallible_s_buf = (*fallible_s).fallible_to_mut().unwrap();
+
+        cs_buf[0] = b'A';
+        cs_buf[1] = b'B';
+        cs_buf[2] = b'C';
+        s_buf[0] = b'A' as u16;
+        s_buf[1] = b'B' as u16;
+        s_buf[2] = b'C' as u16;
+        fallible_cs_buf[0] = b'A';
+        fallible_cs_buf[1] = b'B';
+        fallible_cs_buf[2] = b'C';
+        fallible_s_buf[0] = b'A' as u16;
+        fallible_s_buf[1] = b'B' as u16;
+        fallible_s_buf[2] = b'C' as u16;
+    }
+}
--- a/xpcom/rust/nsstring/src/lib.rs
+++ b/xpcom/rust/nsstring/src/lib.rs
@@ -160,16 +160,17 @@ macro_rules! define_string_types {
         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;
+        begin_writing = $begin_writing: ident, $fallible_begin_writing: 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.
@@ -278,16 +279,55 @@ macro_rules! define_string_types {
                 }
             }
 
             pub fn truncate(&mut self) {
                 unsafe {
                     self.set_length(0);
                 }
             }
+
+            /// Get a `&mut` reference to the backing data for this string.
+            /// This method will allocate and copy if the current backing buffer
+            /// is immutable or shared.
+            pub fn to_mut(&mut self) -> &mut [$char_t] {
+                unsafe {
+                    let len = self.len();
+                    if len == 0 {
+                        // Use an arbitrary non-null value as the pointer
+                        slice::from_raw_parts_mut(0x1 as *mut $char_t, 0)
+                    } else {
+                        slice::from_raw_parts_mut($begin_writing(self), len)
+                    }
+                }
+            }
+
+            /// Get a `&mut` reference to the backing data for this string.
+            /// This method will allocate and copy if the current backing buffer
+            /// is immutable or shared.
+            ///
+            /// Returns `Ok(&mut [T])` on success, and `Err(())` if the
+            /// allocation failed.
+            pub fn fallible_to_mut(&mut self) -> Result<&mut [$char_t], ()> {
+                unsafe {
+                    let len = self.len();
+                    if len == 0 {
+                        // Use an arbitrary non-null value as the pointer
+                        Ok(slice::from_raw_parts_mut(0x1 as *mut $char_t, 0))
+                    } else {
+                        let ptr = $fallible_begin_writing(self);
+                        if ptr.is_null() {
+                            Err(())
+                        } else {
+                            Ok(slice::from_raw_parts_mut(ptr, len))
+                        }
+                    }
+                }
+            }
+
         }
 
         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`
@@ -701,16 +741,17 @@ define_string_types! {
     StringAdapter = nsCStringAdapter;
 
     StringRepr = nsCStringRepr;
 
     drop = Gecko_FinalizeCString;
     assign = Gecko_AssignCString, Gecko_FallibleAssignCString;
     append = Gecko_AppendCString, Gecko_FallibleAppendCString;
     set_length = Gecko_SetLengthCString, Gecko_FallibleSetLengthCString;
+    begin_writing = Gecko_BeginWritingCString, Gecko_FallibleBeginWritingCString;
 }
 
 impl nsACString {
     pub fn assign_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) {
         self.truncate();
         self.append_utf16(other);
     }
 
@@ -823,16 +864,17 @@ define_string_types! {
     StringAdapter = nsStringAdapter;
 
     StringRepr = nsStringRepr;
 
     drop = Gecko_FinalizeString;
     assign = Gecko_AssignString, Gecko_FallibleAssignString;
     append = Gecko_AppendString, Gecko_FallibleAppendString;
     set_length = Gecko_SetLengthString, Gecko_FallibleSetLengthString;
+    begin_writing = Gecko_BeginWritingString, Gecko_FallibleBeginWritingString;
 }
 
 impl nsAString {
     pub fn assign_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) {
         self.truncate();
         self.append_utf8(other);
     }
 
@@ -909,28 +951,32 @@ extern "C" {
     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_SetLengthCString(this: *mut nsACString, length: u32);
+    fn Gecko_BeginWritingCString(this: *mut nsACString) -> *mut u8;
     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_FallibleBeginWritingCString(this: *mut nsACString) -> *mut u8;
 
     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_SetLengthString(this: *mut nsAString, length: u32);
+    fn Gecko_BeginWritingString(this: *mut nsAString) -> *mut u16;
     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;
+    fn Gecko_FallibleBeginWritingString(this: *mut nsAString) -> *mut u16;
 
     // 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;
 }
 
--- a/xpcom/string/nsSubstring.cpp
+++ b/xpcom/string/nsSubstring.cpp
@@ -414,16 +414,26 @@ bool Gecko_FallibleAppendCString(nsACStr
   return aThis->Append(*aOther, mozilla::fallible);
 }
 
 bool Gecko_FallibleSetLengthCString(nsACString* aThis, uint32_t aLength)
 {
   return aThis->SetLength(aLength, mozilla::fallible);
 }
 
+char* Gecko_BeginWritingCString(nsACString* aThis)
+{
+  return aThis->BeginWriting();
+}
+
+char* Gecko_FallibleBeginWritingCString(nsACString* aThis)
+{
+  return aThis->BeginWriting(mozilla::fallible);
+}
+
 void Gecko_FinalizeString(nsAString* aThis)
 {
   aThis->~nsAString();
 }
 
 void Gecko_AssignString(nsAString* aThis, const nsAString* aOther)
 {
   aThis->Assign(*aOther);
@@ -449,9 +459,19 @@ bool Gecko_FallibleAppendString(nsAStrin
   return aThis->Append(*aOther, mozilla::fallible);
 }
 
 bool Gecko_FallibleSetLengthString(nsAString* aThis, uint32_t aLength)
 {
   return aThis->SetLength(aLength, mozilla::fallible);
 }
 
+char16_t* Gecko_BeginWritingString(nsAString* aThis)
+{
+  return aThis->BeginWriting();
+}
+
+char16_t* Gecko_FallibleBeginWritingString(nsAString* aThis)
+{
+  return aThis->BeginWriting(mozilla::fallible);
+}
+
 } // extern "C"