1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5 //! This module provides rust bindings for the XPCOM string types.
6 //!
7 //! # TL;DR (what types should I use)
8 //!
9 //! Use `&{mut,} nsA[C]String` for functions in rust which wish to take or
10 //! mutate XPCOM strings. The other string types `Deref` to this type.
11 //!
12 //! Use `ns[C]String` (`ns[C]String` in C++) for string struct members, and as
13 //! an intermediate between rust string data structures (such as `String` or
14 //! `Vec<u16>`) and `&{mut,} nsA[C]String` (using `ns[C]String::from(value)`).
15 //! These conversions will attempt to re-use the passed-in buffer, appending a
16 //! null.
17 //!
18 //! Use `ns[C]Str` (`nsDependent[C]String` in C++) as an intermediate between
19 //! borrowed rust data structures (such as `&str` and `&[u16]`) and `&{mut,}
20 //! nsA[C]String` (using `ns[C]Str::from(value)`). These conversions should not
21 //! perform any allocations. This type is not safe to share with `C++` as a
22 //! struct field, but passing the borrowed `&{mut,} nsA[C]String` over FFI is
23 //! safe.
24 //!
25 //! Use `*{const,mut} nsA[C]String` (`{const,} nsA[C]String*` in C++) for
26 //! function arguments passed across the rust/C++ language boundary.
27 //!
28 //! There is currently no Rust equivalent to nsAuto[C]String. Implementing a
29 //! type that contains a pointer to an inline buffer is difficult in Rust due
30 //! to its move semantics, which require that it be safe to move a value by
31 //! copying its bits. If such a type is genuinely needed at some point,
32 //! https://bugzilla.mozilla.org/show_bug.cgi?id=1403506#c6 has a sketch of how
33 //! to emulate it via macros.
34 //!
35 //! # String Types
36 //!
37 //! ## `nsA[C]String`
38 //!
39 //! The core types in this module are `nsAString` and `nsACString`. These types
40 //! are zero-sized as far as rust is concerned, and are safe to pass around
41 //! behind both references (in rust code), and pointers (in C++ code). They
42 //! represent a handle to a XPCOM string which holds either `u16` or `u8`
43 //! characters respectively. The backing character buffer is guaranteed to live
44 //! as long as the reference to the `nsAString` or `nsACString`.
45 //!
46 //! These types in rust are simply used as dummy types. References to them
47 //! represent a pointer to the beginning of a variable-sized `#[repr(C)]` struct
48 //! which is common between both C++ and Rust implementations. In C++, their
49 //! corresponding types are also named `nsAString` or `nsACString`, and they are
50 //! defined within the `nsTSubstring.{cpp,h}` file.
51 //!
52 //! ### Valid Operations
53 //!
54 //! An `&nsA[C]String` acts like rust's `&str`, in that it is a borrowed
55 //! reference to the backing data. When used as an argument to other functions
56 //! on `&mut nsA[C]String`, optimizations can be performed to avoid copying
57 //! buffers, as information about the backing storage is preserved.
58 //!
59 //! An `&mut nsA[C]String` acts like rust's `&mut Cow<str>`, in that it is a
60 //! mutable reference to a potentially borrowed string, which when modified will
61 //! ensure that it owns its own backing storage. This type can be appended to
62 //! with the methods `.append`, `.append_utf{8,16}`, and with the `write!`
63 //! macro, and can be assigned to with `.assign`.
64 //!
65 //! ## `ns[C]Str<'a>`
66 //!
67 //! This type is an maybe-owned string type. It acts similarially to a
68 //! `Cow<[{u8,u16}]>`. This type provides `Deref` and `DerefMut` implementations
69 //! to `nsA[C]String`, which provides the methods for manipulating this type.
70 //! This type's lifetime parameter, `'a`, represents the lifetime of the backing
71 //! storage. When modified this type may re-allocate in order to ensure that it
72 //! does not mutate its backing storage.
73 //!
74 //! `ns[C]Str`s can be constructed either with `ns[C]Str::new()`, which creates
75 //! an empty `ns[C]Str<'static>`, or through one of the provided `From`
76 //! implementations. Only `nsCStr` can be constructed `From<'a str>`, as
77 //! constructing a `nsStr` would require transcoding. Use `ns[C]String` instead.
78 //!
79 //! When passing this type by reference, prefer passing a `&nsA[C]String` or
80 //! `&mut nsA[C]String`. to passing this type.
81 //!
82 //! When passing this type across the language boundary, pass it as `*const
83 //! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
84 //! mutable reference.
85 //!
86 //! ## `ns[C]String`
87 //!
88 //! This type is an owned, null-terminated string type. This type provides
89 //! `Deref` and `DerefMut` implementations to `nsA[C]String`, which provides the
90 //! methods for manipulating this type.
91 //!
92 //! `ns[C]String`s can be constructed either with `ns[C]String::new()`, which
93 //! creates an empty `ns[C]String`, or through one of the provided `From`
94 //! implementations, which will try to avoid reallocating when possible,
95 //! although a terminating `null` will be added.
96 //!
97 //! When passing this type by reference, prefer passing a `&nsA[C]String` or
98 //! `&mut nsA[C]String`. to passing this type.
99 //!
100 //! When passing this type across the language boundary, pass it as `*const
101 //! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
102 //! mutable reference. This struct may also be included in `#[repr(C)]` structs
103 //! shared with C++.
104 //!
105 //! ## `ns[C]StringRepr`
106 //!
107 //! This crate also provides the type `ns[C]StringRepr` which acts conceptually
108 //! similar to an `ns[C]String`, however, it does not have a `Drop`
109 //! implementation.
110 //!
111 //! If this type is dropped in rust, it will not free its backing storage. This
112 //! can be useful when implementing FFI types which contain `ns[C]String` members
113 //! which invoke their member's destructors through C++ code.
114
115 #![allow(non_camel_case_types)]
116
117 #[macro_use]
118 extern crate bitflags;
119
120 use std::borrow;
121 use std::cmp;
122 use std::fmt;
123 use std::marker::PhantomData;
124 use std::mem;
125 use std::ops::{Deref, DerefMut};
126 use std::os::raw::c_void;
127 use std::ptr;
128 use std::slice;
129 use std::str;
130 use std::u32;
131
132 mod conversions;
133
134 pub use self::conversions::nscstring_fallible_append_latin1_to_utf8_check;
135 pub use self::conversions::nscstring_fallible_append_utf16_to_latin1_lossy_impl;
136 pub use self::conversions::nscstring_fallible_append_utf16_to_utf8_impl;
137 pub use self::conversions::nscstring_fallible_append_utf8_to_latin1_lossy_check;
138 pub use self::conversions::nsstring_fallible_append_latin1_impl;
139 pub use self::conversions::nsstring_fallible_append_utf8_impl;
140
141 /// A type for showing that `finish()` was called on a `BulkWriteHandle`.
142 /// Instantiating this type from elsewhere is basically an assertion that
143 /// there is no `BulkWriteHandle` around, so be very careful with instantiating
144 /// this type!
145 pub struct BulkWriteOk;
146
147 /// Semi-arbitrary threshold below which we don't care about shrinking
148 /// buffers to size. Currently matches `CACHE_LINE` in the `conversions`
149 /// module.
150 const SHRINKING_THRESHOLD: usize = 64;
151
152 ///////////////////////////////////
153 // Internal Implementation Flags //
154 ///////////////////////////////////
155
156 mod data_flags {
157 bitflags! {
158 // While this has the same layout as u16, it cannot be passed
159 // over FFI safely as a u16.
160 #[repr(C)]
161 pub struct DataFlags: u16 {
162 const TERMINATED = 1 << 0; // IsTerminated returns true
163 const VOIDED = 1 << 1; // IsVoid returns true
164 const REFCOUNTED = 1 << 2; // mData points to a heap-allocated, shareable, refcounted
165 // buffer
166 const OWNED = 1 << 3; // mData points to a heap-allocated, raw buffer
167 const INLINE = 1 << 4; // mData points to a writable, inline buffer
168 const LITERAL = 1 << 5; // mData points to a string literal; TERMINATED will also be set
169 }
170 }
171 }
172
173 mod class_flags {
174 bitflags! {
175 // While this has the same layout as u16, it cannot be passed
176 // over FFI safely as a u16.
177 #[repr(C)]
178 pub struct ClassFlags: u16 {
179 const INLINE = 1 << 0; // |this|'s buffer is inline
180 const NULL_TERMINATED = 1 << 1; // |this| requires its buffer is null-terminated
181 }
182 }
183 }
184
185 use class_flags::ClassFlags;
186 use data_flags::DataFlags;
187
188 ////////////////////////////////////
189 // Generic String Bindings Macros //
190 ////////////////////////////////////
191
192 macro_rules! string_like {
193 {
194 char_t = $char_t: ty;
195
196 AString = $AString: ident;
197 String = $String: ident;
198 Str = $Str: ident;
199
200 StringLike = $StringLike: ident;
201 StringAdapter = $StringAdapter: ident;
202 } => {
203 /// This trait is implemented on types which are `ns[C]String`-like, in
204 /// that they can at very low cost be converted to a borrowed
205 /// `&nsA[C]String`. Unfortunately, the intermediate type
206 /// `ns[C]StringAdapter` is required as well due to types like `&[u8]`
207 /// needing to be (cheaply) wrapped in a `nsCString` on the stack to
208 /// create the `&nsACString`.
209 ///
210 /// This trait is used to DWIM when calling the methods on
211 /// `nsA[C]String`.
212 pub trait $StringLike {
213 fn adapt(&self) -> $StringAdapter;
214 }
215
216 impl<'a, T: $StringLike + ?Sized> $StringLike for &'a T {
217 fn adapt(&self) -> $StringAdapter {
218 <T as $StringLike>::adapt(*self)
219 }
220 }
221
222 impl<'a, T> $StringLike for borrow::Cow<'a, T>
223 where T: $StringLike + borrow::ToOwned + ?Sized {
224 fn adapt(&self) -> $StringAdapter {
225 <T as $StringLike>::adapt(self.as_ref())
226 }
227 }
228
229 impl $StringLike for $AString {
230 fn adapt(&self) -> $StringAdapter {
231 $StringAdapter::Abstract(self)
232 }
233 }
234
235 impl<'a> $StringLike for $Str<'a> {
236 fn adapt(&self) -> $StringAdapter {
237 $StringAdapter::Abstract(self)
238 }
239 }
240
241 impl $StringLike for $String {
242 fn adapt(&self) -> $StringAdapter {
243 $StringAdapter::Abstract(self)
244 }
245 }
246
247 impl $StringLike for [$char_t] {
248 fn adapt(&self) -> $StringAdapter {
249 $StringAdapter::Borrowed($Str::from(self))
250 }
251 }
252
253 impl $StringLike for Vec<$char_t> {
254 fn adapt(&self) -> $StringAdapter {
255 $StringAdapter::Borrowed($Str::from(&self[..]))
256 }
257 }
258
259 impl $StringLike for Box<[$char_t]> {
260 fn adapt(&self) -> $StringAdapter {
261 $StringAdapter::Borrowed($Str::from(&self[..]))
262 }
263 }
264 }
265 }
266
267 impl<'a> Drop for nsAStringBulkWriteHandle<'a> {
268 /// This only runs in error cases. In success cases, `finish()`
269 /// calls `forget(self)`.
drop(&mut self)270 fn drop(&mut self) {
271 if self.capacity == 0 {
272 // If capacity is 0, the string is a zero-length
273 // string, so we have nothing to do.
274 return;
275 }
276 // The old zero terminator may be gone by now, so we need
277 // to write a new one somewhere and make length match.
278 // We can use a length between 1 and self.capacity.
279 // Seems prudent to overwrite the uninitialized memory.
280 // Using the length 1 leaves the shortest memory to overwrite.
281 // U+FFFD is the safest placeholder. Merely truncating the
282 // string to a zero-length string might be dangerous in some
283 // scenarios. See
284 // https://www.unicode.org/reports/tr36/#Substituting_for_Ill_Formed_Subsequences
285 // for closely related scenario.
286 unsafe {
287 let mut this = self.string.as_repr_mut();
288 this.as_mut().length = 1u32;
289 *(this.as_mut().data.as_mut()) = 0xFFFDu16;
290 *(this.as_mut().data.as_ptr().offset(1isize)) = 0;
291 }
292 }
293 }
294
295 impl<'a> Drop for nsACStringBulkWriteHandle<'a> {
296 /// This only runs in error cases. In success cases, `finish()`
297 /// calls `forget(self)`.
drop(&mut self)298 fn drop(&mut self) {
299 if self.capacity == 0 {
300 // If capacity is 0, the string is a zero-length
301 // string, so we have nothing to do.
302 return;
303 }
304 // The old zero terminator may be gone by now, so we need
305 // to write a new one somewhere and make length match.
306 // We can use a length between 1 and self.capacity.
307 // Seems prudent to overwrite the uninitialized memory.
308 // Using the length 1 leaves the shortest memory to overwrite.
309 // U+FFFD is the safest placeholder, but when it doesn't fit,
310 // let's use ASCII substitute. Merely truncating the
311 // string to a zero-length string might be dangerous in some
312 // scenarios. See
313 // https://www.unicode.org/reports/tr36/#Substituting_for_Ill_Formed_Subsequences
314 // for closely related scenario.
315 unsafe {
316 let mut this = self.string.as_repr_mut();
317 if self.capacity >= 3 {
318 this.as_mut().length = 3u32;
319 *(this.as_mut().data.as_mut()) = 0xEFu8;
320 *(this.as_mut().data.as_ptr().offset(1isize)) = 0xBFu8;
321 *(this.as_mut().data.as_ptr().offset(2isize)) = 0xBDu8;
322 *(this.as_mut().data.as_ptr().offset(3isize)) = 0;
323 } else {
324 this.as_mut().length = 1u32;
325 *(this.as_mut().data.as_mut()) = 0x1Au8; // U+FFFD doesn't fit
326 *(this.as_mut().data.as_ptr().offset(1isize)) = 0;
327 }
328 }
329 }
330 }
331
332 macro_rules! define_string_types {
333 {
334 char_t = $char_t: ty;
335
336 AString = $AString: ident;
337 String = $String: ident;
338 Str = $Str: ident;
339
340 StringLike = $StringLike: ident;
341 StringAdapter = $StringAdapter: ident;
342
343 StringRepr = $StringRepr: ident;
344 AutoStringRepr = $AutoStringRepr: ident;
345
346 BulkWriteHandle = $BulkWriteHandle: ident;
347
348 drop = $drop: ident;
349 assign = $assign: ident, $fallible_assign: ident;
350 take_from = $take_from: ident, $fallible_take_from: ident;
351 append = $append: ident, $fallible_append: ident;
352 set_length = $set_length: ident, $fallible_set_length: ident;
353 begin_writing = $begin_writing: ident, $fallible_begin_writing: ident;
354 start_bulk_write = $start_bulk_write: ident;
355 } => {
356 /// The representation of a ns[C]String type in C++. This type is
357 /// used internally by our definition of ns[C]String to ensure layout
358 /// compatibility with the C++ ns[C]String type.
359 ///
360 /// This type may also be used in place of a C++ ns[C]String inside of
361 /// struct definitions which are shared with C++, as it has identical
362 /// layout to our ns[C]String type.
363 ///
364 /// This struct will leak its data if dropped from rust. See the module
365 /// documentation for more information on this type.
366 #[repr(C)]
367 #[derive(Debug)]
368 pub struct $StringRepr {
369 data: ptr::NonNull<$char_t>,
370 length: u32,
371 dataflags: DataFlags,
372 classflags: ClassFlags,
373 }
374
375 impl $StringRepr {
376 fn new(classflags: ClassFlags) -> $StringRepr {
377 static NUL: $char_t = 0;
378 $StringRepr {
379 data: unsafe { ptr::NonNull::new_unchecked(&NUL as *const _ as *mut _) },
380 length: 0,
381 dataflags: DataFlags::TERMINATED | DataFlags::LITERAL,
382 classflags,
383 }
384 }
385 }
386
387 impl Deref for $StringRepr {
388 type Target = $AString;
389 fn deref(&self) -> &$AString {
390 unsafe {
391 mem::transmute(self)
392 }
393 }
394 }
395
396 impl DerefMut for $StringRepr {
397 fn deref_mut(&mut self) -> &mut $AString {
398 unsafe {
399 mem::transmute(self)
400 }
401 }
402 }
403
404 #[repr(C)]
405 #[derive(Debug)]
406 pub struct $AutoStringRepr {
407 super_repr: $StringRepr,
408 inline_capacity: u32,
409 }
410
411 pub struct $BulkWriteHandle<'a> {
412 string: &'a mut $AString,
413 capacity: usize,
414 }
415
416 impl<'a> $BulkWriteHandle<'a> {
417 fn new(string: &'a mut $AString, capacity: usize) -> Self {
418 $BulkWriteHandle{ string, capacity }
419 }
420
421 pub unsafe fn restart_bulk_write(&mut self,
422 capacity: usize,
423 units_to_preserve: usize,
424 allow_shrinking: bool) -> Result<(), ()> {
425 self.capacity =
426 self.string.start_bulk_write_impl(capacity,
427 units_to_preserve,
428 allow_shrinking)?;
429 Ok(())
430 }
431
432 pub fn finish(mut self, length: usize, allow_shrinking: bool) -> BulkWriteOk {
433 // NOTE: Drop is implemented outside the macro earlier in this file,
434 // because it needs to deal with different code unit representations
435 // for the REPLACEMENT CHARACTER in the UTF-16 and UTF-8 cases and
436 // needs to deal with a REPLACEMENT CHARACTER not fitting in the
437 // buffer in the UTF-8 case.
438 assert!(length <= self.capacity);
439 if length == 0 {
440 // `truncate()` is OK even when the string
441 // is in invalid state.
442 self.string.truncate();
443 mem::forget(self); // Don't run the failure path in drop()
444 return BulkWriteOk{};
445 }
446 if allow_shrinking && length > SHRINKING_THRESHOLD {
447 unsafe {
448 let _ = self.restart_bulk_write(length, length, true);
449 }
450 }
451 unsafe {
452 let mut this = self.string.as_repr_mut();
453 this.as_mut().length = length as u32;
454 *(this.as_mut().data.as_ptr().offset(length as isize)) = 0;
455 if cfg!(debug_assertions) {
456 // Overwrite the unused part in debug builds. Note
457 // that capacity doesn't include space for the zero
458 // terminator, so starting after the zero-terminator
459 // we wrote ends up overwriting the terminator space
460 // not reflected in the capacity number.
461 // write_bytes() takes care of multiplying the length
462 // by the size of T.
463 ptr::write_bytes(this.as_mut().data.as_ptr().offset((length + 1) as isize),
464 0xE4u8,
465 self.capacity - length);
466 }
467 // We don't have a Rust interface for mozilla/MemoryChecking.h,
468 // so let's just not communicate with MSan/Valgrind here.
469 }
470 mem::forget(self); // Don't run the failure path in drop()
471 BulkWriteOk{}
472 }
473
474 pub fn as_mut_slice(&mut self) -> &mut [$char_t] {
475 unsafe {
476 let mut this = self.string.as_repr_mut();
477 slice::from_raw_parts_mut(this.as_mut().data.as_ptr(), self.capacity)
478 }
479 }
480 }
481
482 /// This type is the abstract type which is used for interacting with
483 /// strings in rust. Each string type can derefence to an instance of
484 /// this type, which provides the useful operations on strings.
485 ///
486 /// NOTE: Rust thinks this type has a size of 0, because the data
487 /// associated with it is not necessarially safe to move. It is not safe
488 /// to construct a nsAString yourself, unless it is received by
489 /// dereferencing one of these types.
490 ///
491 /// NOTE: The `[u8; 0]` member is zero sized, and only exists to prevent
492 /// the construction by code outside of this module. It is used instead
493 /// of a private `()` member because the `improper_ctypes` lint complains
494 /// about some ZST members in `extern "C"` function declarations.
495 #[repr(C)]
496 pub struct $AString {
497 _prohibit_constructor: [u8; 0],
498 }
499
500 impl $AString {
501 /// Assign the value of `other` into self, overwriting any value
502 /// currently stored. Performs an optimized assignment when possible
503 /// if `other` is a `nsA[C]String`.
504 pub fn assign<T: $StringLike + ?Sized>(&mut self, other: &T) {
505 unsafe { $assign(self, other.adapt().as_ptr()) };
506 }
507
508 /// Assign the value of `other` into self, overwriting any value
509 /// currently stored. Performs an optimized assignment when possible
510 /// if `other` is a `nsA[C]String`.
511 ///
512 /// Returns Ok(()) on success, and Err(()) if the allocation failed.
513 pub fn fallible_assign<T: $StringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
514 if unsafe { $fallible_assign(self, other.adapt().as_ptr()) } {
515 Ok(())
516 } else {
517 Err(())
518 }
519 }
520
521 /// Take the value of `other` and set `self`, overwriting any value
522 /// currently stored. The passed-in string will be truncated.
523 pub fn take_from(&mut self, other: &mut $AString) {
524 unsafe { $take_from(self, other) };
525 }
526
527 /// Take the value of `other` and set `self`, overwriting any value
528 /// currently stored. If this function fails, the source string will
529 /// be left untouched, otherwise it will be truncated.
530 ///
531 /// Returns Ok(()) on success, and Err(()) if the allocation failed.
532 pub fn fallible_take_from(&mut self, other: &mut $AString) -> Result<(), ()> {
533 if unsafe { $fallible_take_from(self, other) } {
534 Ok(())
535 } else {
536 Err(())
537 }
538 }
539
540 /// Append the value of `other` into self.
541 pub fn append<T: $StringLike + ?Sized>(&mut self, other: &T) {
542 unsafe { $append(self, other.adapt().as_ptr()) };
543 }
544
545 /// Append the value of `other` into self.
546 ///
547 /// Returns Ok(()) on success, and Err(()) if the allocation failed.
548 pub fn fallible_append<T: $StringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
549 if unsafe { $fallible_append(self, other.adapt().as_ptr()) } {
550 Ok(())
551 } else {
552 Err(())
553 }
554 }
555
556 /// Set the length of the string to the passed-in length, and expand
557 /// the backing capacity to match. This method is unsafe as it can
558 /// expose uninitialized memory when len is greater than the current
559 /// length of the string.
560 pub unsafe fn set_length(&mut self, len: u32) {
561 $set_length(self, len);
562 }
563
564 /// Set the length of the string to the passed-in length, and expand
565 /// the backing capacity to match. This method is unsafe as it can
566 /// expose uninitialized memory when len is greater than the current
567 /// length of the string.
568 ///
569 /// Returns Ok(()) on success, and Err(()) if the allocation failed.
570 pub unsafe fn fallible_set_length(&mut self, len: u32) -> Result<(), ()> {
571 if $fallible_set_length(self, len) {
572 Ok(())
573 } else {
574 Err(())
575 }
576 }
577
578 pub fn truncate(&mut self) {
579 unsafe {
580 self.set_length(0);
581 }
582 }
583
584 /// Get a `&mut` reference to the backing data for this string.
585 /// This method will allocate and copy if the current backing buffer
586 /// is immutable or shared.
587 pub fn to_mut(&mut self) -> &mut [$char_t] {
588 unsafe {
589 let len = self.len();
590 if len == 0 {
591 // Use an arbitrary but aligned non-null value as the pointer
592 slice::from_raw_parts_mut(ptr::NonNull::<$char_t>::dangling().as_ptr(), 0)
593 } else {
594 slice::from_raw_parts_mut($begin_writing(self), len)
595 }
596 }
597 }
598
599 /// Get a `&mut` reference to the backing data for this string.
600 /// This method will allocate and copy if the current backing buffer
601 /// is immutable or shared.
602 ///
603 /// Returns `Ok(&mut [T])` on success, and `Err(())` if the
604 /// allocation failed.
605 pub fn fallible_to_mut(&mut self) -> Result<&mut [$char_t], ()> {
606 unsafe {
607 let len = self.len();
608 if len == 0 {
609 // Use an arbitrary but aligned non-null value as the pointer
610 Ok(slice::from_raw_parts_mut(
611 ptr::NonNull::<$char_t>::dangling().as_ptr() as *mut $char_t, 0))
612 } else {
613 let ptr = $fallible_begin_writing(self);
614 if ptr.is_null() {
615 Err(())
616 } else {
617 Ok(slice::from_raw_parts_mut(ptr, len))
618 }
619 }
620 }
621 }
622
623 /// Unshares the buffer of the string and returns a handle
624 /// from which a writable slice whose length is the rounded-up
625 /// capacity can be obtained.
626 ///
627 /// Fails also if the new length doesn't fit in 32 bits.
628 ///
629 /// # Safety
630 ///
631 /// Unsafe because of exposure of uninitialized memory.
632 pub unsafe fn bulk_write(&mut self,
633 capacity: usize,
634 units_to_preserve: usize,
635 allow_shrinking: bool) -> Result<$BulkWriteHandle, ()> {
636 let capacity =
637 self.start_bulk_write_impl(capacity, units_to_preserve, allow_shrinking)?;
638 Ok($BulkWriteHandle::new(self, capacity))
639 }
640
641 unsafe fn start_bulk_write_impl(&mut self,
642 capacity: usize,
643 units_to_preserve: usize,
644 allow_shrinking: bool) -> Result<usize, ()> {
645 if capacity > u32::max_value() as usize {
646 Err(())
647 } else {
648 let capacity32 = capacity as u32;
649 let rounded = $start_bulk_write(self,
650 capacity32,
651 units_to_preserve as u32,
652 allow_shrinking && capacity > SHRINKING_THRESHOLD);
653 if rounded == u32::max_value() {
654 return Err(())
655 }
656 Ok(rounded as usize)
657 }
658 }
659
660 fn as_repr(&self) -> &$StringRepr {
661 // All $AString values point to a struct prefix which is
662 // identical to $StringRepr, this we can transmute `self`
663 // into $StringRepr to get the reference to the underlying
664 // data.
665 unsafe {
666 &*(self as *const _ as *const $StringRepr)
667 }
668 }
669
670 fn as_repr_mut(&mut self) -> ptr::NonNull<$StringRepr> {
671 unsafe { ptr::NonNull::new_unchecked(self as *mut _ as *mut $StringRepr)}
672 }
673
674 fn as_auto_string_repr(&self) -> Option<&$AutoStringRepr> {
675 if !self.as_repr().classflags.contains(ClassFlags::INLINE) {
676 return None;
677 }
678
679 unsafe {
680 Some(&*(self as *const _ as *const $AutoStringRepr))
681 }
682 }
683
684 /// If this is an autostring, returns the capacity (excluding the
685 /// zero terminator) of the inline buffer within `Some()`. Otherwise
686 /// returns `None`.
687 pub fn inline_capacity(&self) -> Option<usize> {
688 Some(self.as_auto_string_repr()?.inline_capacity as usize)
689 }
690 }
691
692 impl Deref for $AString {
693 type Target = [$char_t];
694 fn deref(&self) -> &[$char_t] {
695 unsafe {
696 // All $AString values point to a struct prefix which is
697 // identical to $StringRepr, this we can transmute `self`
698 // into $StringRepr to get the reference to the underlying
699 // data.
700 let this: &$StringRepr = mem::transmute(self);
701 slice::from_raw_parts(this.data.as_ptr(), this.length as usize)
702 }
703 }
704 }
705
706 impl AsRef<[$char_t]> for $AString {
707 fn as_ref(&self) -> &[$char_t] {
708 self
709 }
710 }
711
712 impl cmp::PartialEq for $AString {
713 fn eq(&self, other: &$AString) -> bool {
714 &self[..] == &other[..]
715 }
716 }
717
718 impl cmp::PartialEq<[$char_t]> for $AString {
719 fn eq(&self, other: &[$char_t]) -> bool {
720 &self[..] == other
721 }
722 }
723
724 impl cmp::PartialEq<$String> for $AString {
725 fn eq(&self, other: &$String) -> bool {
726 self.eq(&**other)
727 }
728 }
729
730 impl<'a> cmp::PartialEq<$Str<'a>> for $AString {
731 fn eq(&self, other: &$Str<'a>) -> bool {
732 self.eq(&**other)
733 }
734 }
735
736 #[repr(C)]
737 pub struct $Str<'a> {
738 hdr: $StringRepr,
739 _marker: PhantomData<&'a [$char_t]>,
740 }
741
742 impl $Str<'static> {
743 pub fn new() -> $Str<'static> {
744 $Str {
745 hdr: $StringRepr::new(ClassFlags::empty()),
746 _marker: PhantomData,
747 }
748 }
749 }
750
751 impl<'a> Drop for $Str<'a> {
752 fn drop(&mut self) {
753 unsafe {
754 $drop(&mut **self);
755 }
756 }
757 }
758
759 impl<'a> Deref for $Str<'a> {
760 type Target = $AString;
761 fn deref(&self) -> &$AString {
762 &self.hdr
763 }
764 }
765
766 impl<'a> DerefMut for $Str<'a> {
767 fn deref_mut(&mut self) -> &mut $AString {
768 &mut self.hdr
769 }
770 }
771
772 impl<'a> AsRef<[$char_t]> for $Str<'a> {
773 fn as_ref(&self) -> &[$char_t] {
774 &self
775 }
776 }
777
778 impl<'a> From<&'a [$char_t]> for $Str<'a> {
779 fn from(s: &'a [$char_t]) -> $Str<'a> {
780 assert!(s.len() < (u32::MAX as usize));
781 if s.is_empty() {
782 return $Str::new();
783 }
784 $Str {
785 hdr: $StringRepr {
786 data: unsafe { ptr::NonNull::new_unchecked(s.as_ptr() as *mut _) },
787 length: s.len() as u32,
788 dataflags: DataFlags::empty(),
789 classflags: ClassFlags::empty(),
790 },
791 _marker: PhantomData,
792 }
793 }
794 }
795
796 impl<'a> From<&'a Vec<$char_t>> for $Str<'a> {
797 fn from(s: &'a Vec<$char_t>) -> $Str<'a> {
798 $Str::from(&s[..])
799 }
800 }
801
802 impl<'a> From<&'a $AString> for $Str<'a> {
803 fn from(s: &'a $AString) -> $Str<'a> {
804 $Str::from(&s[..])
805 }
806 }
807
808 impl<'a> fmt::Write for $Str<'a> {
809 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
810 $AString::write_str(self, s)
811 }
812 }
813
814 impl<'a> fmt::Display for $Str<'a> {
815 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
816 <$AString as fmt::Display>::fmt(self, f)
817 }
818 }
819
820 impl<'a> fmt::Debug for $Str<'a> {
821 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
822 <$AString as fmt::Debug>::fmt(self, f)
823 }
824 }
825
826 impl<'a> cmp::PartialEq for $Str<'a> {
827 fn eq(&self, other: &$Str<'a>) -> bool {
828 $AString::eq(self, other)
829 }
830 }
831
832 impl<'a> cmp::PartialEq<[$char_t]> for $Str<'a> {
833 fn eq(&self, other: &[$char_t]) -> bool {
834 $AString::eq(self, other)
835 }
836 }
837
838 impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $Str<'a> {
839 fn eq(&self, other: &&'b [$char_t]) -> bool {
840 $AString::eq(self, *other)
841 }
842 }
843
844 impl<'a> cmp::PartialEq<str> for $Str<'a> {
845 fn eq(&self, other: &str) -> bool {
846 $AString::eq(self, other)
847 }
848 }
849
850 impl<'a, 'b> cmp::PartialEq<&'b str> for $Str<'a> {
851 fn eq(&self, other: &&'b str) -> bool {
852 $AString::eq(self, *other)
853 }
854 }
855
856 #[repr(C)]
857 pub struct $String {
858 hdr: $StringRepr,
859 }
860
861 unsafe impl Send for $String {}
862 unsafe impl Sync for $String {}
863
864 impl $String {
865 pub fn new() -> $String {
866 $String {
867 hdr: $StringRepr::new(ClassFlags::NULL_TERMINATED),
868 }
869 }
870
871 /// Converts this String into a StringRepr, which will leak if the
872 /// repr is not passed to something that knows how to free it.
873 pub fn into_repr(mut self) -> $StringRepr {
874 mem::replace(&mut self.hdr, $StringRepr::new(ClassFlags::NULL_TERMINATED))
875 }
876 }
877
878 impl Drop for $String {
879 fn drop(&mut self) {
880 unsafe {
881 $drop(&mut **self);
882 }
883 }
884 }
885
886 impl Deref for $String {
887 type Target = $AString;
888 fn deref(&self) -> &$AString {
889 &self.hdr
890 }
891 }
892
893 impl DerefMut for $String {
894 fn deref_mut(&mut self) -> &mut $AString {
895 &mut self.hdr
896 }
897 }
898
899 impl Clone for $String {
900 fn clone(&self) -> Self {
901 let mut copy = $String::new();
902 copy.assign(self);
903 copy
904 }
905 }
906
907 impl AsRef<[$char_t]> for $String {
908 fn as_ref(&self) -> &[$char_t] {
909 &self
910 }
911 }
912
913 impl<'a> From<&'a [$char_t]> for $String {
914 fn from(s: &'a [$char_t]) -> $String {
915 let mut res = $String::new();
916 res.assign(&$Str::from(&s[..]));
917 res
918 }
919 }
920
921 impl<'a> From<&'a Vec<$char_t>> for $String {
922 fn from(s: &'a Vec<$char_t>) -> $String {
923 $String::from(&s[..])
924 }
925 }
926
927 impl<'a> From<&'a $AString> for $String {
928 fn from(s: &'a $AString) -> $String {
929 $String::from(&s[..])
930 }
931 }
932
933 impl From<Box<[$char_t]>> for $String {
934 fn from(s: Box<[$char_t]>) -> $String {
935 s.into_vec().into()
936 }
937 }
938
939 impl From<Vec<$char_t>> for $String {
940 fn from(mut s: Vec<$char_t>) -> $String {
941 assert!(s.len() < (u32::MAX as usize));
942 if s.is_empty() {
943 return $String::new();
944 }
945
946 let length = s.len() as u32;
947 s.push(0); // null terminator
948
949 // SAFETY NOTE: This method produces an data_flags::OWNED
950 // ns[C]String from a Box<[$char_t]>. this is only safe
951 // because in the Gecko tree, we use the same allocator for
952 // Rust code as for C++ code, meaning that our box can be
953 // legally freed with libc::free().
954 let ptr = s.as_mut_ptr();
955 mem::forget(s);
956 unsafe {
957 Gecko_IncrementStringAdoptCount(ptr as *mut _);
958 }
959 $String {
960 hdr: $StringRepr {
961 data: unsafe { ptr::NonNull::new_unchecked(ptr) },
962 length: length,
963 dataflags: DataFlags::OWNED | DataFlags::TERMINATED,
964 classflags: ClassFlags::NULL_TERMINATED,
965 }
966 }
967 }
968 }
969
970 impl fmt::Write for $String {
971 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
972 $AString::write_str(self, s)
973 }
974 }
975
976 impl fmt::Display for $String {
977 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
978 <$AString as fmt::Display>::fmt(self, f)
979 }
980 }
981
982 impl fmt::Debug for $String {
983 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
984 <$AString as fmt::Debug>::fmt(self, f)
985 }
986 }
987
988 impl cmp::PartialEq for $String {
989 fn eq(&self, other: &$String) -> bool {
990 $AString::eq(self, other)
991 }
992 }
993
994 impl cmp::PartialEq<[$char_t]> for $String {
995 fn eq(&self, other: &[$char_t]) -> bool {
996 $AString::eq(self, other)
997 }
998 }
999
1000 impl<'a> cmp::PartialEq<&'a [$char_t]> for $String {
1001 fn eq(&self, other: &&'a [$char_t]) -> bool {
1002 $AString::eq(self, *other)
1003 }
1004 }
1005
1006 impl cmp::PartialEq<str> for $String {
1007 fn eq(&self, other: &str) -> bool {
1008 $AString::eq(self, other)
1009 }
1010 }
1011
1012 impl<'a> cmp::PartialEq<&'a str> for $String {
1013 fn eq(&self, other: &&'a str) -> bool {
1014 $AString::eq(self, *other)
1015 }
1016 }
1017
1018 /// An adapter type to allow for passing both types which coerce to
1019 /// &[$char_type], and &$AString to a function, while still performing
1020 /// optimized operations when passed the $AString.
1021 pub enum $StringAdapter<'a> {
1022 Borrowed($Str<'a>),
1023 Abstract(&'a $AString),
1024 }
1025
1026 impl<'a> $StringAdapter<'a> {
1027 fn as_ptr(&self) -> *const $AString {
1028 &**self
1029 }
1030 }
1031
1032 impl<'a> Deref for $StringAdapter<'a> {
1033 type Target = $AString;
1034
1035 fn deref(&self) -> &$AString {
1036 match *self {
1037 $StringAdapter::Borrowed(ref s) => s,
1038 $StringAdapter::Abstract(ref s) => s,
1039 }
1040 }
1041 }
1042
1043 impl<'a> $StringAdapter<'a> {
1044 #[allow(dead_code)]
1045 fn is_abstract(&self) -> bool {
1046 match *self {
1047 $StringAdapter::Borrowed(_) => false,
1048 $StringAdapter::Abstract(_) => true,
1049 }
1050 }
1051 }
1052
1053 string_like! {
1054 char_t = $char_t;
1055
1056 AString = $AString;
1057 String = $String;
1058 Str = $Str;
1059
1060 StringLike = $StringLike;
1061 StringAdapter = $StringAdapter;
1062 }
1063 }
1064 }
1065
1066 ///////////////////////////////////////////
1067 // Bindings for nsCString (u8 char type) //
1068 ///////////////////////////////////////////
1069
1070 define_string_types! {
1071 char_t = u8;
1072
1073 AString = nsACString;
1074 String = nsCString;
1075 Str = nsCStr;
1076
1077 StringLike = nsCStringLike;
1078 StringAdapter = nsCStringAdapter;
1079
1080 StringRepr = nsCStringRepr;
1081 AutoStringRepr = nsAutoCStringRepr;
1082
1083 BulkWriteHandle = nsACStringBulkWriteHandle;
1084
1085 drop = Gecko_FinalizeCString;
1086 assign = Gecko_AssignCString, Gecko_FallibleAssignCString;
1087 take_from = Gecko_TakeFromCString, Gecko_FallibleTakeFromCString;
1088 append = Gecko_AppendCString, Gecko_FallibleAppendCString;
1089 set_length = Gecko_SetLengthCString, Gecko_FallibleSetLengthCString;
1090 begin_writing = Gecko_BeginWritingCString, Gecko_FallibleBeginWritingCString;
1091 start_bulk_write = Gecko_StartBulkWriteCString;
1092 }
1093
1094 impl nsACString {
1095 /// Gets a CString as an utf-8 str or a String, trying to avoid copies, and
1096 /// replacing invalid unicode sequences with replacement characters.
1097 #[inline]
to_utf8(&self) -> borrow::Cow<str>1098 pub fn to_utf8(&self) -> borrow::Cow<str> {
1099 String::from_utf8_lossy(&self[..])
1100 }
1101
1102 #[inline]
as_str_unchecked(&self) -> &str1103 pub unsafe fn as_str_unchecked(&self) -> &str {
1104 if cfg!(debug_assertions) {
1105 str::from_utf8(self).expect("Should be utf-8")
1106 } else {
1107 str::from_utf8_unchecked(self)
1108 }
1109 }
1110 }
1111
1112 impl<'a> From<&'a str> for nsCStr<'a> {
from(s: &'a str) -> nsCStr<'a>1113 fn from(s: &'a str) -> nsCStr<'a> {
1114 s.as_bytes().into()
1115 }
1116 }
1117
1118 impl<'a> From<&'a String> for nsCStr<'a> {
from(s: &'a String) -> nsCStr<'a>1119 fn from(s: &'a String) -> nsCStr<'a> {
1120 nsCStr::from(&s[..])
1121 }
1122 }
1123
1124 impl<'a> From<&'a str> for nsCString {
from(s: &'a str) -> nsCString1125 fn from(s: &'a str) -> nsCString {
1126 s.as_bytes().into()
1127 }
1128 }
1129
1130 impl<'a> From<&'a String> for nsCString {
from(s: &'a String) -> nsCString1131 fn from(s: &'a String) -> nsCString {
1132 nsCString::from(&s[..])
1133 }
1134 }
1135
1136 impl From<Box<str>> for nsCString {
from(s: Box<str>) -> nsCString1137 fn from(s: Box<str>) -> nsCString {
1138 s.into_string().into()
1139 }
1140 }
1141
1142 impl From<String> for nsCString {
from(s: String) -> nsCString1143 fn from(s: String) -> nsCString {
1144 s.into_bytes().into()
1145 }
1146 }
1147
1148 // Support for the write!() macro for appending to nsACStrings
1149 impl fmt::Write for nsACString {
write_str(&mut self, s: &str) -> Result<(), fmt::Error>1150 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
1151 self.append(s);
1152 Ok(())
1153 }
1154 }
1155
1156 impl fmt::Display for nsACString {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>1157 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
1158 fmt::Display::fmt(&self.to_utf8(), f)
1159 }
1160 }
1161
1162 impl fmt::Debug for nsACString {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>1163 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
1164 fmt::Debug::fmt(&self.to_utf8(), f)
1165 }
1166 }
1167
1168 impl cmp::PartialEq<str> for nsACString {
eq(&self, other: &str) -> bool1169 fn eq(&self, other: &str) -> bool {
1170 &self[..] == other.as_bytes()
1171 }
1172 }
1173
1174 impl nsCStringLike for str {
adapt(&self) -> nsCStringAdapter1175 fn adapt(&self) -> nsCStringAdapter {
1176 nsCStringAdapter::Borrowed(nsCStr::from(self))
1177 }
1178 }
1179
1180 impl nsCStringLike for String {
adapt(&self) -> nsCStringAdapter1181 fn adapt(&self) -> nsCStringAdapter {
1182 nsCStringAdapter::Borrowed(nsCStr::from(&self[..]))
1183 }
1184 }
1185
1186 impl nsCStringLike for Box<str> {
adapt(&self) -> nsCStringAdapter1187 fn adapt(&self) -> nsCStringAdapter {
1188 nsCStringAdapter::Borrowed(nsCStr::from(&self[..]))
1189 }
1190 }
1191
1192 // This trait is implemented on types which are Latin1 `nsCString`-like,
1193 // in that they can at very low cost be converted to a borrowed
1194 // `&nsACString` and do not denote UTF-8ness in the Rust type system.
1195 //
1196 // This trait is used to DWIM when calling the methods on
1197 // `nsACString`.
1198 string_like! {
1199 char_t = u8;
1200
1201 AString = nsACString;
1202 String = nsCString;
1203 Str = nsCStr;
1204
1205 StringLike = Latin1StringLike;
1206 StringAdapter = nsCStringAdapter;
1207 }
1208
1209 ///////////////////////////////////////////
1210 // Bindings for nsString (u16 char type) //
1211 ///////////////////////////////////////////
1212
1213 define_string_types! {
1214 char_t = u16;
1215
1216 AString = nsAString;
1217 String = nsString;
1218 Str = nsStr;
1219
1220 StringLike = nsStringLike;
1221 StringAdapter = nsStringAdapter;
1222
1223 StringRepr = nsStringRepr;
1224 AutoStringRepr = nsAutoStringRepr;
1225
1226 BulkWriteHandle = nsAStringBulkWriteHandle;
1227
1228 drop = Gecko_FinalizeString;
1229 assign = Gecko_AssignString, Gecko_FallibleAssignString;
1230 take_from = Gecko_TakeFromString, Gecko_FallibleTakeFromString;
1231 append = Gecko_AppendString, Gecko_FallibleAppendString;
1232 set_length = Gecko_SetLengthString, Gecko_FallibleSetLengthString;
1233 begin_writing = Gecko_BeginWritingString, Gecko_FallibleBeginWritingString;
1234 start_bulk_write = Gecko_StartBulkWriteString;
1235 }
1236
1237 // NOTE: The From impl for a string slice for nsString produces a <'static>
1238 // lifetime, as it allocates.
1239 impl<'a> From<&'a str> for nsString {
from(s: &'a str) -> nsString1240 fn from(s: &'a str) -> nsString {
1241 s.encode_utf16().collect::<Vec<u16>>().into()
1242 }
1243 }
1244
1245 impl<'a> From<&'a String> for nsString {
from(s: &'a String) -> nsString1246 fn from(s: &'a String) -> nsString {
1247 nsString::from(&s[..])
1248 }
1249 }
1250
1251 // Support for the write!() macro for writing to nsStrings
1252 impl fmt::Write for nsAString {
write_str(&mut self, s: &str) -> Result<(), fmt::Error>1253 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
1254 // Directly invoke gecko's routines for appending utf8 strings to
1255 // nsAString values, to avoid as much overhead as possible
1256 self.append_str(s);
1257 Ok(())
1258 }
1259 }
1260
1261 impl nsAString {
1262 /// Turns this utf-16 string into a string, replacing invalid unicode
1263 /// sequences with replacement characters.
1264 ///
1265 /// This is needed because the default ToString implementation goes through
1266 /// fmt::Display, and thus allocates the string twice.
to_string(&self) -> String1267 pub fn to_string(&self) -> String {
1268 String::from_utf16_lossy(&self[..])
1269 }
1270 }
1271
1272 impl fmt::Display for nsAString {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>1273 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
1274 fmt::Display::fmt(&self.to_string(), f)
1275 }
1276 }
1277
1278 impl fmt::Debug for nsAString {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>1279 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
1280 fmt::Debug::fmt(&self.to_string(), f)
1281 }
1282 }
1283
1284 impl cmp::PartialEq<str> for nsAString {
eq(&self, other: &str) -> bool1285 fn eq(&self, other: &str) -> bool {
1286 other.encode_utf16().eq(self.iter().cloned())
1287 }
1288 }
1289
1290 #[cfg(not(feature = "gecko_debug"))]
1291 #[allow(non_snake_case)]
Gecko_IncrementStringAdoptCount(_: *mut c_void)1292 unsafe fn Gecko_IncrementStringAdoptCount(_: *mut c_void) {}
1293
1294 extern "C" {
1295 #[cfg(feature = "gecko_debug")]
Gecko_IncrementStringAdoptCount(data: *mut c_void)1296 fn Gecko_IncrementStringAdoptCount(data: *mut c_void);
1297
1298 // Gecko implementation in nsSubstring.cpp
Gecko_FinalizeCString(this: *mut nsACString)1299 fn Gecko_FinalizeCString(this: *mut nsACString);
1300
Gecko_AssignCString(this: *mut nsACString, other: *const nsACString)1301 fn Gecko_AssignCString(this: *mut nsACString, other: *const nsACString);
Gecko_TakeFromCString(this: *mut nsACString, other: *mut nsACString)1302 fn Gecko_TakeFromCString(this: *mut nsACString, other: *mut nsACString);
Gecko_AppendCString(this: *mut nsACString, other: *const nsACString)1303 fn Gecko_AppendCString(this: *mut nsACString, other: *const nsACString);
Gecko_SetLengthCString(this: *mut nsACString, length: u32)1304 fn Gecko_SetLengthCString(this: *mut nsACString, length: u32);
Gecko_BeginWritingCString(this: *mut nsACString) -> *mut u81305 fn Gecko_BeginWritingCString(this: *mut nsACString) -> *mut u8;
Gecko_FallibleAssignCString(this: *mut nsACString, other: *const nsACString) -> bool1306 fn Gecko_FallibleAssignCString(this: *mut nsACString, other: *const nsACString) -> bool;
Gecko_FallibleTakeFromCString(this: *mut nsACString, other: *mut nsACString) -> bool1307 fn Gecko_FallibleTakeFromCString(this: *mut nsACString, other: *mut nsACString) -> bool;
Gecko_FallibleAppendCString(this: *mut nsACString, other: *const nsACString) -> bool1308 fn Gecko_FallibleAppendCString(this: *mut nsACString, other: *const nsACString) -> bool;
Gecko_FallibleSetLengthCString(this: *mut nsACString, length: u32) -> bool1309 fn Gecko_FallibleSetLengthCString(this: *mut nsACString, length: u32) -> bool;
Gecko_FallibleBeginWritingCString(this: *mut nsACString) -> *mut u81310 fn Gecko_FallibleBeginWritingCString(this: *mut nsACString) -> *mut u8;
Gecko_StartBulkWriteCString( this: *mut nsACString, capacity: u32, units_to_preserve: u32, allow_shrinking: bool, ) -> u321311 fn Gecko_StartBulkWriteCString(
1312 this: *mut nsACString,
1313 capacity: u32,
1314 units_to_preserve: u32,
1315 allow_shrinking: bool,
1316 ) -> u32;
1317
Gecko_FinalizeString(this: *mut nsAString)1318 fn Gecko_FinalizeString(this: *mut nsAString);
1319
Gecko_AssignString(this: *mut nsAString, other: *const nsAString)1320 fn Gecko_AssignString(this: *mut nsAString, other: *const nsAString);
Gecko_TakeFromString(this: *mut nsAString, other: *mut nsAString)1321 fn Gecko_TakeFromString(this: *mut nsAString, other: *mut nsAString);
Gecko_AppendString(this: *mut nsAString, other: *const nsAString)1322 fn Gecko_AppendString(this: *mut nsAString, other: *const nsAString);
Gecko_SetLengthString(this: *mut nsAString, length: u32)1323 fn Gecko_SetLengthString(this: *mut nsAString, length: u32);
Gecko_BeginWritingString(this: *mut nsAString) -> *mut u161324 fn Gecko_BeginWritingString(this: *mut nsAString) -> *mut u16;
Gecko_FallibleAssignString(this: *mut nsAString, other: *const nsAString) -> bool1325 fn Gecko_FallibleAssignString(this: *mut nsAString, other: *const nsAString) -> bool;
Gecko_FallibleTakeFromString(this: *mut nsAString, other: *mut nsAString) -> bool1326 fn Gecko_FallibleTakeFromString(this: *mut nsAString, other: *mut nsAString) -> bool;
Gecko_FallibleAppendString(this: *mut nsAString, other: *const nsAString) -> bool1327 fn Gecko_FallibleAppendString(this: *mut nsAString, other: *const nsAString) -> bool;
Gecko_FallibleSetLengthString(this: *mut nsAString, length: u32) -> bool1328 fn Gecko_FallibleSetLengthString(this: *mut nsAString, length: u32) -> bool;
Gecko_FallibleBeginWritingString(this: *mut nsAString) -> *mut u161329 fn Gecko_FallibleBeginWritingString(this: *mut nsAString) -> *mut u16;
Gecko_StartBulkWriteString( this: *mut nsAString, capacity: u32, units_to_preserve: u32, allow_shrinking: bool, ) -> u321330 fn Gecko_StartBulkWriteString(
1331 this: *mut nsAString,
1332 capacity: u32,
1333 units_to_preserve: u32,
1334 allow_shrinking: bool,
1335 ) -> u32;
1336 }
1337
1338 //////////////////////////////////////
1339 // Repr Validation Helper Functions //
1340 //////////////////////////////////////
1341
1342 pub mod test_helpers {
1343 //! This module only exists to help with ensuring that the layout of the
1344 //! structs inside of rust and C++ are identical.
1345 //!
1346 //! It is public to ensure that these testing functions are avaliable to
1347 //! gtest code.
1348
1349 use super::{nsACString, nsAString};
1350 use super::{nsCStr, nsCString, nsCStringRepr};
1351 use super::{nsStr, nsString, nsStringRepr};
1352 use super::{ClassFlags, DataFlags};
1353 use std::mem;
1354
1355 /// Generates an #[no_mangle] extern "C" function which returns the size and
1356 /// alignment of the given type with the given name.
1357 macro_rules! size_align_check {
1358 ($T:ty, $fname:ident) => {
1359 #[no_mangle]
1360 #[allow(non_snake_case)]
1361 pub extern "C" fn $fname(size: *mut usize, align: *mut usize) {
1362 unsafe {
1363 *size = mem::size_of::<$T>();
1364 *align = mem::align_of::<$T>();
1365 }
1366 }
1367 };
1368 ($T:ty, $U:ty, $V:ty, $fname:ident) => {
1369 #[no_mangle]
1370 #[allow(non_snake_case)]
1371 pub extern "C" fn $fname(size: *mut usize, align: *mut usize) {
1372 unsafe {
1373 *size = mem::size_of::<$T>();
1374 *align = mem::align_of::<$T>();
1375
1376 assert_eq!(*size, mem::size_of::<$U>());
1377 assert_eq!(*align, mem::align_of::<$U>());
1378 assert_eq!(*size, mem::size_of::<$V>());
1379 assert_eq!(*align, mem::align_of::<$V>());
1380 }
1381 }
1382 };
1383 }
1384
1385 size_align_check!(
1386 nsStringRepr,
1387 nsString,
1388 nsStr<'static>,
1389 Rust_Test_ReprSizeAlign_nsString
1390 );
1391 size_align_check!(
1392 nsCStringRepr,
1393 nsCString,
1394 nsCStr<'static>,
1395 Rust_Test_ReprSizeAlign_nsCString
1396 );
1397
1398 /// Generates a $[no_mangle] extern "C" function which returns the size,
1399 /// alignment and offset in the parent struct of a given member, with the
1400 /// given name.
1401 ///
1402 /// This method can trigger Undefined Behavior if the accessing the member
1403 /// $member on a given type would use that type's `Deref` implementation.
1404 macro_rules! member_check {
1405 ($T:ty, $U:ty, $V:ty, $member:ident, $method:ident) => {
1406 #[no_mangle]
1407 #[allow(non_snake_case)]
1408 pub extern "C" fn $method(size: *mut usize, align: *mut usize, offset: *mut usize) {
1409 unsafe {
1410 // Create a temporary value of type T to get offsets, sizes
1411 // and alignments from.
1412 let tmp: mem::MaybeUninit<$T> = mem::MaybeUninit::uninit();
1413 // FIXME: This should use &raw references when available,
1414 // this is technically UB as it creates a reference to
1415 // uninitialized memory, but there's no better way to do
1416 // this right now.
1417 let tmp = &*tmp.as_ptr();
1418 *size = mem::size_of_val(&tmp.$member);
1419 *align = mem::align_of_val(&tmp.$member);
1420 *offset = (&tmp.$member as *const _ as usize) - (tmp as *const $T as usize);
1421
1422 let tmp: mem::MaybeUninit<$U> = mem::MaybeUninit::uninit();
1423 let tmp = &*tmp.as_ptr();
1424 assert_eq!(*size, mem::size_of_val(&tmp.hdr.$member));
1425 assert_eq!(*align, mem::align_of_val(&tmp.hdr.$member));
1426 assert_eq!(
1427 *offset,
1428 (&tmp.hdr.$member as *const _ as usize) - (tmp as *const $U as usize)
1429 );
1430
1431 let tmp: mem::MaybeUninit<$V> = mem::MaybeUninit::uninit();
1432 let tmp = &*tmp.as_ptr();
1433 assert_eq!(*size, mem::size_of_val(&tmp.hdr.$member));
1434 assert_eq!(*align, mem::align_of_val(&tmp.hdr.$member));
1435 assert_eq!(
1436 *offset,
1437 (&tmp.hdr.$member as *const _ as usize) - (tmp as *const $V as usize)
1438 );
1439 }
1440 }
1441 };
1442 }
1443
1444 member_check!(
1445 nsStringRepr,
1446 nsString,
1447 nsStr<'static>,
1448 data,
1449 Rust_Test_Member_nsString_mData
1450 );
1451 member_check!(
1452 nsStringRepr,
1453 nsString,
1454 nsStr<'static>,
1455 length,
1456 Rust_Test_Member_nsString_mLength
1457 );
1458 member_check!(
1459 nsStringRepr,
1460 nsString,
1461 nsStr<'static>,
1462 dataflags,
1463 Rust_Test_Member_nsString_mDataFlags
1464 );
1465 member_check!(
1466 nsStringRepr,
1467 nsString,
1468 nsStr<'static>,
1469 classflags,
1470 Rust_Test_Member_nsString_mClassFlags
1471 );
1472 member_check!(
1473 nsCStringRepr,
1474 nsCString,
1475 nsCStr<'static>,
1476 data,
1477 Rust_Test_Member_nsCString_mData
1478 );
1479 member_check!(
1480 nsCStringRepr,
1481 nsCString,
1482 nsCStr<'static>,
1483 length,
1484 Rust_Test_Member_nsCString_mLength
1485 );
1486 member_check!(
1487 nsCStringRepr,
1488 nsCString,
1489 nsCStr<'static>,
1490 dataflags,
1491 Rust_Test_Member_nsCString_mDataFlags
1492 );
1493 member_check!(
1494 nsCStringRepr,
1495 nsCString,
1496 nsCStr<'static>,
1497 classflags,
1498 Rust_Test_Member_nsCString_mClassFlags
1499 );
1500
1501 #[no_mangle]
1502 #[allow(non_snake_case)]
Rust_Test_NsStringFlags( f_terminated: *mut u16, f_voided: *mut u16, f_refcounted: *mut u16, f_owned: *mut u16, f_inline: *mut u16, f_literal: *mut u16, f_class_inline: *mut u16, f_class_null_terminated: *mut u16, )1503 pub extern "C" fn Rust_Test_NsStringFlags(
1504 f_terminated: *mut u16,
1505 f_voided: *mut u16,
1506 f_refcounted: *mut u16,
1507 f_owned: *mut u16,
1508 f_inline: *mut u16,
1509 f_literal: *mut u16,
1510 f_class_inline: *mut u16,
1511 f_class_null_terminated: *mut u16,
1512 ) {
1513 unsafe {
1514 *f_terminated = DataFlags::TERMINATED.bits();
1515 *f_voided = DataFlags::VOIDED.bits();
1516 *f_refcounted = DataFlags::REFCOUNTED.bits();
1517 *f_owned = DataFlags::OWNED.bits();
1518 *f_inline = DataFlags::INLINE.bits();
1519 *f_literal = DataFlags::LITERAL.bits();
1520 *f_class_inline = ClassFlags::INLINE.bits();
1521 *f_class_null_terminated = ClassFlags::NULL_TERMINATED.bits();
1522 }
1523 }
1524
1525 #[no_mangle]
1526 #[allow(non_snake_case)]
Rust_InlineCapacityFromRust( cstring: *const nsACString, string: *const nsAString, cstring_capacity: *mut usize, string_capacity: *mut usize, )1527 pub extern "C" fn Rust_InlineCapacityFromRust(
1528 cstring: *const nsACString,
1529 string: *const nsAString,
1530 cstring_capacity: *mut usize,
1531 string_capacity: *mut usize,
1532 ) {
1533 unsafe {
1534 *cstring_capacity = (*cstring).inline_capacity().unwrap();
1535 *string_capacity = (*string).inline_capacity().unwrap();
1536 }
1537 }
1538 }
1539