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