1 //! This module provides rust bindings for the XPCOM string types.
2 //!
3 //! # TL;DR (what types should I use)
4 //!
5 //! Use `&{mut,} nsA[C]String` for functions in rust which wish to take or
6 //! mutate XPCOM strings. The other string types `Deref` to this type.
7 //!
8 //! Use `ns[C]String<'a>` for string struct members which don't leave rust, and
9 //! as an intermediate between rust string data structures (such as `String`,
10 //! `Vec<u16>`, `&str`, and `&[u16]`) and `&{mut,} nsA[C]String` (using
11 //! `ns[C]String::from(value)`). These conversions, when possible, will not
12 //! perform any allocations.
13 //!
14 //! Use `nsFixed[C]String` or `ns_auto_[c]string!` for dynamic stack allocated
15 //! strings which are expected to hold short string values.
16 //!
17 //! Use `*{const,mut} nsA[C]String` (`{const,} nsA[C]String*` in C++) for
18 //! function arguments passed across the rust/C++ language boundary.
19 //!
20 //! Use `ns[C]StringRepr` for string struct members which are shared between
21 //! rust and C++, but be careful, because this type lacks a `Drop`
22 //! implementation.
23 //!
24 //! # String Types
25 //!
26 //! ## `nsA[C]String`
27 //!
28 //! The core types in this module are `nsAString` and `nsACString`. These types
29 //! are zero-sized as far as rust is concerned, and are safe to pass around
30 //! behind both references (in rust code), and pointers (in C++ code). They
31 //! represent a handle to a XPCOM string which holds either `u16` or `u8`
32 //! characters respectively. The backing character buffer is guaranteed to live
33 //! as long as the reference to the `nsAString` or `nsACString`.
34 //!
35 //! These types in rust are simply used as dummy types. References to them
36 //! represent a pointer to the beginning of a variable-sized `#[repr(C)]` struct
37 //! which is common between both C++ and Rust implementations. In C++, their
38 //! corresponding types are also named `nsAString` or `nsACString`, and they are
39 //! defined within the `nsTSubstring.{cpp,h}` file.
40 //!
41 //! ### Valid Operations
42 //!
43 //! An `&nsA[C]String` acts like rust's `&str`, in that it is a borrowed
44 //! reference to the backing data. When used as an argument to other functions
45 //! on `&mut nsA[C]String`, optimizations can be performed to avoid copying
46 //! buffers, as information about the backing storage is preserved.
47 //!
48 //! An `&mut nsA[C]String` acts like rust's `&mut Cow<str>`, in that it is a
49 //! mutable reference to a potentially borrowed string, which when modified will
50 //! ensure that it owns its own backing storage. This type can be appended to
51 //! with the methods `.append`, `.append_utf{8,16}`, and with the `write!`
52 //! macro, and can be assigned to with `.assign`.
53 //!
54 //! ## `ns[C]String<'a>`
55 //!
56 //! This type is an maybe-owned string type. It acts similarially to a
57 //! `Cow<[{u8,u16}]>`. This type provides `Deref` and `DerefMut` implementations
58 //! to `nsA[C]String`, which provides the methods for manipulating this type.
59 //! This type's lifetime parameter, `'a`, represents the lifetime of the backing
60 //! storage. When modified this type may re-allocate in order to ensure that it
61 //! does not mutate its backing storage.
62 //!
63 //! `ns[C]String`s can be constructed either with `ns[C]String::new()`, which
64 //! creates an empty `ns[C]String<'static>`, or through one of the provided
65 //! `From` implementations. Both string types may be constructed `From<&'a
66 //! str>`, with `nsCString` having a `'a` lifetime, as the storage is shared
67 //! with the `str`, while `nsString` has a `'static` lifetime, as its storage
68 //! has to be transcoded.
69 //!
70 //! When passing this type by reference, prefer passing a `&nsA[C]String` or
71 //! `&mut nsA[C]String`. to passing this type.
72 //!
73 //! This type is _not_ `#[repr(C)]`, as it has a `Drop` impl, which in versions
74 //! of `rustc < 1.13` adds drop flags to the struct, which messes up the layout,
75 //! making it unsafe to pass across the FFI boundary. The rust compiler will
76 //! warn if this type appears in `extern "C"` function definitions.
77 //!
78 //! When passing this type across the language boundary, pass it as `*const
79 //! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
80 //! mutable reference.
81 //!
82 //! This type is similar to the C++ type of the same name.
83 //!
84 //! ## `nsFixed[C]String<'a>`
85 //!
86 //! This type is a string type with fixed backing storage. It is created with
87 //! `nsFixed[C]String::new(buffer)`, passing a mutable reference to a buffer as
88 //! the argument. This buffer will be used as backing storage whenever the
89 //! resulting string will fit within it, falling back to heap allocations only
90 //! when the string size exceeds that of the backing buffer.
91 //!
92 //! Like `ns[C]String`, this type dereferences to `nsA[C]String` which provides
93 //! the methods for manipulating the type, and is not `#[repr(C)]`.
94 //!
95 //! When passing this type by reference, prefer passing a `&nsA[C]String` or
96 //! `&mut nsA[C]String`. to passing this type.
97 //!
98 //! This type is _not_ `#[repr(C)]`, as it has a `Drop` impl, which in versions
99 //! of `rustc < 1.13` adds drop flags to the struct, which messes up the layout,
100 //! making it unsafe to pass across the FFI boundary. The rust compiler will
101 //! warn if this type appears in `extern "C"` function definitions.
102 //!
103 //! When passing this type across the language boundary, pass it as `*const
104 //! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
105 //! mutable reference.
106 //!
107 //! This type is similar to the C++ type of the same name.
108 //!
109 //! ## `ns_auto_[c]string!($name)`
110 //!
111 //! This is a helper macro which defines a fixed size, (currently 64 character),
112 //! backing array on the stack, and defines a local variable with name `$name`
113 //! which is a `nsFixed[C]String` using this buffer as its backing storage.
114 //!
115 //! Usage of this macro is similar to the C++ type `nsAuto[C]String`, but could
116 //! not be implemented as a basic type due to the differences between rust and
117 //! C++'s move semantics.
118 //!
119 //! ## `ns[C]StringRepr`
120 //!
121 //! This type represents a C++ `ns[C]String`. This type is `#[repr(C)]` and is
122 //! safe to use in struct definitions which are shared across the language
123 //! boundary. It automatically dereferences to `&{mut,} nsA[C]String`, and thus
124 //! can be treated similarially to `ns[C]String`.
125 //!
126 //! If this type is dropped in rust, it will not free its backing storage. This
127 //! is because types implementing `Drop` have a drop flag added, which messes up
128 //! the layout of this type. When drop flags are removed, which should happen in
129 //! `rustc 1.13` (see rust-lang/rust#35764), this type will likely be removed,
130 //! and replaced with direct usage of `ns[C]String<'a>`, as its layout may be
131 //! identical. This module provides rust bindings to our xpcom ns[C]String
132 //! types.
133 
134 #![allow(non_camel_case_types)]
135 
136 use std::ops::{Deref, DerefMut};
137 use std::marker::PhantomData;
138 use std::slice;
139 use std::ptr;
140 use std::mem;
141 use std::fmt;
142 use std::cmp;
143 use std::str;
144 use std::u32;
145 
146 //////////////////////////////////
147 // Internal Implemenation Flags //
148 //////////////////////////////////
149 
150 const F_NONE: u32 = 0; // no flags
151 
152 // data flags are in the lower 16-bits
153 const F_TERMINATED: u32 = 1 << 0; // IsTerminated returns true
154 const F_VOIDED: u32 = 1 << 1; // IsVoid returns true
155 const F_SHARED: u32 = 1 << 2; // mData points to a heap-allocated, shared buffer
156 const F_OWNED: u32 = 1 << 3; // mData points to a heap-allocated, raw buffer
157 const F_FIXED: u32 = 1 << 4; // mData points to a fixed-size writable, dependent buffer
158 const F_LITERAL: u32 = 1 << 5; // mData points to a string literal; F_TERMINATED will also be set
159 
160 // class flags are in the upper 16-bits
161 const F_CLASS_FIXED: u32 = 1 << 16; // indicates that |this| is of type nsTFixedString
162 
163 ////////////////////////////////////
164 // Generic String Bindings Macros //
165 ////////////////////////////////////
166 
167 macro_rules! define_string_types {
168     {
169         char_t = $char_t: ty;
170         AString = $AString: ident;
171         String = $String: ident;
172         FixedString = $FixedString: ident;
173 
174         StringRepr = $StringRepr: ident;
175         FixedStringRepr = $FixedStringRepr: ident;
176         AutoStringRepr = $AutoStringRepr: ident;
177     } => {
178         /// The representation of a ns[C]String type in C++. This type is
179         /// used internally by our definition of ns[C]String to ensure layout
180         /// compatibility with the C++ ns[C]String type.
181         ///
182         /// This type may also be used in place of a C++ ns[C]String inside of
183         /// struct definitions which are shared with C++, as it has identical
184         /// layout to our ns[C]String type. Due to drop flags, our ns[C]String
185         /// type does not have identical layout. When drop flags are removed,
186         /// this type will likely be made a private implementation detail, and
187         /// its uses will be replaced with `ns[C]String`.
188         ///
189         /// This struct will leak its data if dropped from rust. See the module
190         /// documentation for more information on this type.
191         #[repr(C)]
192         pub struct $StringRepr {
193             data: *const $char_t,
194             length: u32,
195             flags: u32,
196         }
197 
198         impl Deref for $StringRepr {
199             type Target = $AString;
200             fn deref(&self) -> &$AString {
201                 unsafe {
202                     mem::transmute(self)
203                 }
204             }
205         }
206 
207         impl DerefMut for $StringRepr {
208             fn deref_mut(&mut self) -> &mut $AString {
209                 unsafe {
210                     mem::transmute(self)
211                 }
212             }
213         }
214 
215         /// The representation of a nsFixed[C]String type in C++. This type is
216         /// used internally by our definition of nsFixed[C]String to ensure layout
217         /// compatibility with the C++ nsFixed[C]String type.
218         #[repr(C)]
219         struct $FixedStringRepr {
220             base: $StringRepr,
221             capacity: u32,
222             buffer: *mut $char_t,
223         }
224 
225         /// This type is the abstract type which is used for interacting with
226         /// strings in rust. Each string type can derefence to an instance of
227         /// this type, which provides the useful operations on strings.
228         ///
229         /// NOTE: Rust thinks this type has a size of 0, because the data
230         /// associated with it is not necessarially safe to move. It is not safe
231         /// to construct a nsAString yourself, unless it is received by
232         /// dereferencing one of these types.
233         ///
234         /// NOTE: The `[u8; 0]` member is zero sized, and only exists to prevent
235         /// the construction by code outside of this module. It is used instead
236         /// of a private `()` member because the `improper_ctypes` lint complains
237         /// about some ZST members in `extern "C"` function declarations.
238         #[repr(C)]
239         pub struct $AString {
240             _prohibit_constructor: [u8; 0],
241         }
242 
243         impl Deref for $AString {
244             type Target = [$char_t];
245             fn deref(&self) -> &[$char_t] {
246                 unsafe {
247                     // This is legal, as all $AString values actually point to a
248                     // $StringRepr
249                     let this: &$StringRepr = mem::transmute(self);
250                     if this.data.is_null() {
251                         debug_assert!(this.length == 0);
252                         // Use an arbitrary non-null value as the pointer
253                         slice::from_raw_parts(0x1 as *const $char_t, 0)
254                     } else {
255                         slice::from_raw_parts(this.data, this.length as usize)
256                     }
257                 }
258             }
259         }
260 
261         impl cmp::PartialEq for $AString {
262             fn eq(&self, other: &$AString) -> bool {
263                 &self[..] == &other[..]
264             }
265         }
266 
267         impl cmp::PartialEq<[$char_t]> for $AString {
268             fn eq(&self, other: &[$char_t]) -> bool {
269                 &self[..] == other
270             }
271         }
272 
273         impl<'a> cmp::PartialEq<$String<'a>> for $AString {
274             fn eq(&self, other: &$String<'a>) -> bool {
275                 self.eq(&**other)
276             }
277         }
278 
279         impl<'a> cmp::PartialEq<$FixedString<'a>> for $AString {
280             fn eq(&self, other: &$FixedString<'a>) -> bool {
281                 self.eq(&**other)
282             }
283         }
284 
285         pub struct $String<'a> {
286             hdr: $StringRepr,
287             _marker: PhantomData<&'a [$char_t]>,
288         }
289 
290         impl $String<'static> {
291             pub fn new() -> $String<'static> {
292                 $String {
293                     hdr: $StringRepr {
294                         data: ptr::null(),
295                         length: 0,
296                         flags: F_NONE,
297                     },
298                     _marker: PhantomData,
299                 }
300             }
301         }
302 
303         impl<'a> Deref for $String<'a> {
304             type Target = $AString;
305             fn deref(&self) -> &$AString {
306                 &self.hdr
307             }
308         }
309 
310         impl<'a> DerefMut for $String<'a> {
311             fn deref_mut(&mut self) -> &mut $AString {
312                 &mut self.hdr
313             }
314         }
315 
316         impl<'a> From<&'a String> for $String<'a> {
317             fn from(s: &'a String) -> $String<'a> {
318                 $String::from(&s[..])
319             }
320         }
321 
322         impl<'a> From<&'a Vec<$char_t>> for $String<'a> {
323             fn from(s: &'a Vec<$char_t>) -> $String<'a> {
324                 $String::from(&s[..])
325             }
326         }
327 
328         impl<'a> From<&'a [$char_t]> for $String<'a> {
329             fn from(s: &'a [$char_t]) -> $String<'a> {
330                 assert!(s.len() < (u32::MAX as usize));
331                 $String {
332                     hdr: $StringRepr {
333                         data: s.as_ptr(),
334                         length: s.len() as u32,
335                         flags: F_NONE,
336                     },
337                     _marker: PhantomData,
338                 }
339             }
340         }
341 
342         impl From<Box<[$char_t]>> for $String<'static> {
343             fn from(s: Box<[$char_t]>) -> $String<'static> {
344                 assert!(s.len() < (u32::MAX as usize));
345                 // SAFETY NOTE: This method produces an F_OWNED ns[C]String from
346                 // a Box<[$char_t]>. this is only safe because in the Gecko
347                 // tree, we use the same allocator for Rust code as for C++
348                 // code, meaning that our box can be legally freed with
349                 // libc::free().
350                 let length = s.len() as u32;
351                 let ptr = s.as_ptr();
352                 mem::forget(s);
353                 $String {
354                     hdr: $StringRepr {
355                         data: ptr,
356                         length: length,
357                         flags: F_OWNED,
358                     },
359                     _marker: PhantomData,
360                 }
361             }
362         }
363 
364         impl From<Vec<$char_t>> for $String<'static> {
365             fn from(s: Vec<$char_t>) -> $String<'static> {
366                 s.into_boxed_slice().into()
367             }
368         }
369 
370         impl<'a> From<&'a $AString> for $String<'static> {
371             fn from(s: &'a $AString) -> $String<'static> {
372                 let mut string = $String::new();
373                 string.assign(s);
374                 string
375             }
376         }
377 
378         impl<'a> fmt::Write for $String<'a> {
379             fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
380                 $AString::write_str(self, s)
381             }
382         }
383 
384         impl<'a> fmt::Display for $String<'a> {
385             fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
386                 <$AString as fmt::Display>::fmt(self, f)
387             }
388         }
389 
390         impl<'a> fmt::Debug for $String<'a> {
391             fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
392                 <$AString as fmt::Debug>::fmt(self, f)
393             }
394         }
395 
396         impl<'a> cmp::PartialEq for $String<'a> {
397             fn eq(&self, other: &$String<'a>) -> bool {
398                 $AString::eq(self, other)
399             }
400         }
401 
402         impl<'a> cmp::PartialEq<[$char_t]> for $String<'a> {
403             fn eq(&self, other: &[$char_t]) -> bool {
404                 $AString::eq(self, other)
405             }
406         }
407 
408         impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $String<'a> {
409             fn eq(&self, other: &&'b [$char_t]) -> bool {
410                 $AString::eq(self, *other)
411             }
412         }
413 
414         impl<'a> cmp::PartialEq<str> for $String<'a> {
415             fn eq(&self, other: &str) -> bool {
416                 $AString::eq(self, other)
417             }
418         }
419 
420         impl<'a, 'b> cmp::PartialEq<&'b str> for $String<'a> {
421             fn eq(&self, other: &&'b str) -> bool {
422                 $AString::eq(self, *other)
423             }
424         }
425 
426         impl<'a> Drop for $String<'a> {
427             fn drop(&mut self) {
428                 unsafe {
429                     self.finalize();
430                 }
431             }
432         }
433 
434         /// A nsFixed[C]String is a string which uses a fixed size mutable
435         /// backing buffer for storing strings which will fit within that
436         /// buffer, rather than using heap allocations.
437         pub struct $FixedString<'a> {
438             hdr: $FixedStringRepr,
439             _marker: PhantomData<&'a mut [$char_t]>,
440         }
441 
442         impl<'a> $FixedString<'a> {
443             pub fn new(buf: &'a mut [$char_t]) -> $FixedString<'a> {
444                 let len = buf.len();
445                 assert!(len < (u32::MAX as usize));
446                 let buf_ptr = buf.as_mut_ptr();
447                 $FixedString {
448                     hdr: $FixedStringRepr {
449                         base: $StringRepr {
450                             data: ptr::null(),
451                             length: 0,
452                             flags: F_CLASS_FIXED,
453                         },
454                         capacity: len as u32,
455                         buffer: buf_ptr,
456                     },
457                     _marker: PhantomData,
458                 }
459             }
460         }
461 
462         impl<'a> Deref for $FixedString<'a> {
463             type Target = $AString;
464             fn deref(&self) -> &$AString {
465                 &self.hdr.base
466             }
467         }
468 
469         impl<'a> DerefMut for $FixedString<'a> {
470             fn deref_mut(&mut self) -> &mut $AString {
471                 &mut self.hdr.base
472             }
473         }
474 
475         impl<'a> fmt::Write for $FixedString<'a> {
476             fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
477                 $AString::write_str(self, s)
478             }
479         }
480 
481         impl<'a> fmt::Display for $FixedString<'a> {
482             fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
483                 <$AString as fmt::Display>::fmt(self, f)
484             }
485         }
486 
487         impl<'a> fmt::Debug for $FixedString<'a> {
488             fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
489                 <$AString as fmt::Debug>::fmt(self, f)
490             }
491         }
492 
493         impl<'a> cmp::PartialEq for $FixedString<'a> {
494             fn eq(&self, other: &$FixedString<'a>) -> bool {
495                 $AString::eq(self, other)
496             }
497         }
498 
499         impl<'a> cmp::PartialEq<[$char_t]> for $FixedString<'a> {
500             fn eq(&self, other: &[$char_t]) -> bool {
501                 $AString::eq(self, other)
502             }
503         }
504 
505         impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $FixedString<'a> {
506             fn eq(&self, other: &&'b [$char_t]) -> bool {
507                 $AString::eq(self, *other)
508             }
509         }
510 
511         impl<'a> cmp::PartialEq<str> for $FixedString<'a> {
512             fn eq(&self, other: &str) -> bool {
513                 $AString::eq(self, other)
514             }
515         }
516 
517         impl<'a, 'b> cmp::PartialEq<&'b str> for $FixedString<'a> {
518             fn eq(&self, other: &&'b str) -> bool {
519                 $AString::eq(self, *other)
520             }
521         }
522 
523         impl<'a> Drop for $FixedString<'a> {
524             fn drop(&mut self) {
525                 unsafe {
526                     self.finalize();
527                 }
528             }
529         }
530     }
531 }
532 
533 ///////////////////////////////////////////
534 // Bindings for nsCString (u8 char type) //
535 ///////////////////////////////////////////
536 
537 define_string_types! {
538     char_t = u8;
539 
540     AString = nsACString;
541     String = nsCString;
542     FixedString = nsFixedCString;
543 
544     StringRepr = nsCStringRepr;
545     FixedStringRepr = nsFixedCStringRepr;
546     AutoStringRepr = nsAutoCStringRepr;
547 }
548 
549 impl nsACString {
550     /// Leaves the nsACString in an unstable state with a dangling data pointer.
551     /// Should only be used in drop implementations of rust types which wrap
552     /// this type.
finalize(&mut self)553     unsafe fn finalize(&mut self) {
554         Gecko_FinalizeCString(self);
555     }
556 
assign(&mut self, other: &nsACString)557     pub fn assign(&mut self, other: &nsACString) {
558         unsafe {
559             Gecko_AssignCString(self as *mut _, other as *const _);
560         }
561     }
562 
assign_utf16(&mut self, other: &nsAString)563     pub fn assign_utf16(&mut self, other: &nsAString) {
564         self.assign(&nsCString::new());
565         self.append_utf16(other);
566     }
567 
append(&mut self, other: &nsACString)568     pub fn append(&mut self, other: &nsACString) {
569         unsafe {
570             Gecko_AppendCString(self as *mut _, other as *const _);
571         }
572     }
573 
append_utf16(&mut self, other: &nsAString)574     pub fn append_utf16(&mut self, other: &nsAString) {
575         unsafe {
576             Gecko_AppendUTF16toCString(self as *mut _, other as *const _);
577         }
578     }
579 
as_str_unchecked(&self) -> &str580     pub unsafe fn as_str_unchecked(&self) -> &str {
581         str::from_utf8_unchecked(self)
582     }
583 }
584 
585 impl<'a> From<&'a str> for nsCString<'a> {
from(s: &'a str) -> nsCString<'a>586     fn from(s: &'a str) -> nsCString<'a> {
587         s.as_bytes().into()
588     }
589 }
590 
591 impl From<Box<str>> for nsCString<'static> {
from(s: Box<str>) -> nsCString<'static>592     fn from(s: Box<str>) -> nsCString<'static> {
593         s.into_string().into()
594     }
595 }
596 
597 impl From<String> for nsCString<'static> {
from(s: String) -> nsCString<'static>598     fn from(s: String) -> nsCString<'static> {
599         s.into_bytes().into()
600     }
601 }
602 
603 // Support for the write!() macro for appending to nsACStrings
604 impl fmt::Write for nsACString {
write_str(&mut self, s: &str) -> Result<(), fmt::Error>605     fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
606         self.append(&nsCString::from(s));
607         Ok(())
608     }
609 }
610 
611 impl fmt::Display for nsACString {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>612     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
613         fmt::Display::fmt(&String::from_utf8_lossy(&self[..]), f)
614     }
615 }
616 
617 impl fmt::Debug for nsACString {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>618     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
619         fmt::Debug::fmt(&String::from_utf8_lossy(&self[..]), f)
620     }
621 }
622 
623 impl cmp::PartialEq<str> for nsACString {
eq(&self, other: &str) -> bool624     fn eq(&self, other: &str) -> bool {
625         &self[..] == other.as_bytes()
626     }
627 }
628 
629 #[macro_export]
630 macro_rules! ns_auto_cstring {
631     ($name:ident) => {
632         let mut buf: [u8; 64] = [0; 64];
633         let mut $name = $crate::nsFixedCString::new(&mut buf);
634     }
635 }
636 
637 ///////////////////////////////////////////
638 // Bindings for nsString (u16 char type) //
639 ///////////////////////////////////////////
640 
641 define_string_types! {
642     char_t = u16;
643 
644     AString = nsAString;
645     String = nsString;
646     FixedString = nsFixedString;
647 
648     StringRepr = nsStringRepr;
649     FixedStringRepr = nsFixedStringRepr;
650     AutoStringRepr = nsAutoStringRepr;
651 }
652 
653 impl nsAString {
654     /// Leaves the nsAString in an unstable state with a dangling data pointer.
655     /// Should only be used in drop implementations of rust types which wrap
656     /// this type.
finalize(&mut self)657     unsafe fn finalize(&mut self) {
658         Gecko_FinalizeString(self);
659     }
660 
assign(&mut self, other: &nsAString)661     pub fn assign(&mut self, other: &nsAString) {
662         unsafe {
663             Gecko_AssignString(self as *mut _, other as *const _);
664         }
665     }
666 
assign_utf8(&mut self, other: &nsACString)667     pub fn assign_utf8(&mut self, other: &nsACString) {
668         self.assign(&nsString::new());
669         self.append_utf8(other);
670     }
671 
append(&mut self, other: &nsAString)672     pub fn append(&mut self, other: &nsAString) {
673         unsafe {
674             Gecko_AppendString(self as *mut _, other as *const _);
675         }
676     }
677 
append_utf8(&mut self, other: &nsACString)678     pub fn append_utf8(&mut self, other: &nsACString) {
679         unsafe {
680             Gecko_AppendUTF8toString(self as *mut _, other as *const _);
681         }
682     }
683 }
684 
685 // NOTE: The From impl for a string slice for nsString produces a <'static>
686 // lifetime, as it allocates.
687 impl<'a> From<&'a str> for nsString<'static> {
from(s: &'a str) -> nsString<'static>688     fn from(s: &'a str) -> nsString<'static> {
689         s.encode_utf16().collect::<Vec<u16>>().into()
690     }
691 }
692 
693 // Support for the write!() macro for writing to nsStrings
694 impl fmt::Write for nsAString {
write_str(&mut self, s: &str) -> Result<(), fmt::Error>695     fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
696         // Directly invoke gecko's routines for appending utf8 strings to
697         // nsAString values, to avoid as much overhead as possible
698         self.append_utf8(&nsCString::from(s));
699         Ok(())
700     }
701 }
702 
703 impl fmt::Display for nsAString {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>704     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
705         fmt::Display::fmt(&String::from_utf16_lossy(&self[..]), f)
706     }
707 }
708 
709 impl fmt::Debug for nsAString {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>710     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
711         fmt::Debug::fmt(&String::from_utf16_lossy(&self[..]), f)
712     }
713 }
714 
715 impl cmp::PartialEq<str> for nsAString {
eq(&self, other: &str) -> bool716     fn eq(&self, other: &str) -> bool {
717         other.encode_utf16().eq(self.iter().cloned())
718     }
719 }
720 
721 #[macro_export]
722 macro_rules! ns_auto_string {
723     ($name:ident) => {
724         let mut buf: [u16; 64] = [0; 64];
725         let mut $name = $crate::nsFixedString::new(&mut buf);
726     }
727 }
728 
729 // NOTE: These bindings currently only expose infallible operations. Perhaps
730 // consider allowing for fallible methods?
731 extern "C" {
732     // Gecko implementation in nsSubstring.cpp
Gecko_FinalizeCString(this: *mut nsACString)733     fn Gecko_FinalizeCString(this: *mut nsACString);
Gecko_AssignCString(this: *mut nsACString, other: *const nsACString)734     fn Gecko_AssignCString(this: *mut nsACString, other: *const nsACString);
Gecko_AppendCString(this: *mut nsACString, other: *const nsACString)735     fn Gecko_AppendCString(this: *mut nsACString, other: *const nsACString);
736 
Gecko_FinalizeString(this: *mut nsAString)737     fn Gecko_FinalizeString(this: *mut nsAString);
Gecko_AssignString(this: *mut nsAString, other: *const nsAString)738     fn Gecko_AssignString(this: *mut nsAString, other: *const nsAString);
Gecko_AppendString(this: *mut nsAString, other: *const nsAString)739     fn Gecko_AppendString(this: *mut nsAString, other: *const nsAString);
740 
741     // Gecko implementation in nsReadableUtils.cpp
Gecko_AppendUTF16toCString(this: *mut nsACString, other: *const nsAString)742     fn Gecko_AppendUTF16toCString(this: *mut nsACString, other: *const nsAString);
Gecko_AppendUTF8toString(this: *mut nsAString, other: *const nsACString)743     fn Gecko_AppendUTF8toString(this: *mut nsAString, other: *const nsACString);
744 }
745 
746 //////////////////////////////////////
747 // Repr Validation Helper Functions //
748 //////////////////////////////////////
749 
750 pub mod test_helpers {
751     //! This module only exists to help with ensuring that the layout of the
752     //! structs inside of rust and C++ are identical.
753     //!
754     //! It is public to ensure that these testing functions are avaliable to
755     //! gtest code.
756 
757     use super::{
758         nsFixedCStringRepr,
759         nsFixedStringRepr,
760         nsCStringRepr,
761         nsStringRepr,
762         F_NONE,
763         F_TERMINATED,
764         F_VOIDED,
765         F_SHARED,
766         F_OWNED,
767         F_FIXED,
768         F_LITERAL,
769         F_CLASS_FIXED,
770     };
771     use std::mem;
772 
773     /// Generates an #[no_mangle] extern "C" function which returns the size and
774     /// alignment of the given type with the given name.
775     macro_rules! size_align_check {
776         ($T:ty, $fname:ident) => {
777             #[no_mangle]
778             #[allow(non_snake_case)]
779             pub extern fn $fname(size: *mut usize, align: *mut usize) {
780                 unsafe {
781                     *size = mem::size_of::<$T>();
782                     *align = mem::align_of::<$T>();
783                 }
784             }
785         }
786     }
787 
788     size_align_check!(nsStringRepr, Rust_Test_ReprSizeAlign_nsString);
789     size_align_check!(nsCStringRepr, Rust_Test_ReprSizeAlign_nsCString);
790     size_align_check!(nsFixedStringRepr, Rust_Test_ReprSizeAlign_nsFixedString);
791     size_align_check!(nsFixedCStringRepr, Rust_Test_ReprSizeAlign_nsFixedCString);
792 
793     /// Generates a $[no_mangle] extern "C" function which returns the size,
794     /// alignment and offset in the parent struct of a given member, with the
795     /// given name.
796     ///
797     /// This method can trigger Undefined Behavior if the accessing the member
798     /// $member on a given type would use that type's `Deref` implementation.
799     macro_rules! member_check {
800         ($T:ty, $member:ident, $method:ident) => {
801             #[no_mangle]
802             #[allow(non_snake_case)]
803             pub extern fn $method(size: *mut usize,
804                                   align: *mut usize,
805                                   offset: *mut usize) {
806                 unsafe {
807                     // Create a temporary value of type T to get offsets, sizes
808                     // and aligns off of
809                     let tmp: $T = mem::zeroed();
810                     *size = mem::size_of_val(&tmp.$member);
811                     *align = mem::align_of_val(&tmp.$member);
812                     *offset =
813                         (&tmp.$member as *const _ as usize) -
814                         (&tmp as *const _ as usize);
815                     mem::forget(tmp);
816                 }
817             }
818         }
819     }
820 
821     member_check!(nsStringRepr, data, Rust_Test_Member_nsString_mData);
822     member_check!(nsStringRepr, length, Rust_Test_Member_nsString_mLength);
823     member_check!(nsStringRepr, flags, Rust_Test_Member_nsString_mFlags);
824     member_check!(nsCStringRepr, data, Rust_Test_Member_nsCString_mData);
825     member_check!(nsCStringRepr, length, Rust_Test_Member_nsCString_mLength);
826     member_check!(nsCStringRepr, flags, Rust_Test_Member_nsCString_mFlags);
827     member_check!(nsFixedStringRepr, capacity, Rust_Test_Member_nsFixedString_mFixedCapacity);
828     member_check!(nsFixedStringRepr, buffer, Rust_Test_Member_nsFixedString_mFixedBuf);
829     member_check!(nsFixedCStringRepr, capacity, Rust_Test_Member_nsFixedCString_mFixedCapacity);
830     member_check!(nsFixedCStringRepr, buffer, Rust_Test_Member_nsFixedCString_mFixedBuf);
831 
832     #[no_mangle]
833     #[allow(non_snake_case)]
Rust_Test_NsStringFlags(f_none: *mut u32, f_terminated: *mut u32, f_voided: *mut u32, f_shared: *mut u32, f_owned: *mut u32, f_fixed: *mut u32, f_literal: *mut u32, f_class_fixed: *mut u32)834     pub extern fn Rust_Test_NsStringFlags(f_none: *mut u32,
835                                           f_terminated: *mut u32,
836                                           f_voided: *mut u32,
837                                           f_shared: *mut u32,
838                                           f_owned: *mut u32,
839                                           f_fixed: *mut u32,
840                                           f_literal: *mut u32,
841                                           f_class_fixed: *mut u32) {
842         unsafe {
843             *f_none = F_NONE;
844             *f_terminated = F_TERMINATED;
845             *f_voided = F_VOIDED;
846             *f_shared = F_SHARED;
847             *f_owned = F_OWNED;
848             *f_fixed = F_FIXED;
849             *f_literal = F_LITERAL;
850             *f_class_fixed = F_CLASS_FIXED;
851         }
852     }
853 }
854