Bug 1403506 - Remove nsTFixedString<T>. r=erahm.

(patch is actually r=erahm,mystor)

nsTFixedString<T> is only used as a base class for nsTAutoStringN<T, N>, so
this patch merges the former into the latter, cutting some code and simplifying
the string class hierarchy.

Because the "Fixed" name is now gone, the patch also renames
StringDataFlags::FIXED as INLINE and ClassDataFlags::FIXED as INLINE.

The patch also removes nsFixed[C]String and ns_auto_[c]string! from Rust code
because nsAutoString can't be implemented directly in Rust due to its move
semantics. There were only two uses of ns_auto_string! outside of tests so this
seems like a minor loss.

MozReview-Commit-ID: 8ntximghiut

--HG--
extra : rebase_source : f36edbae0553adcfee356fb8b311097ff7424786
This commit is contained in:
Nicholas Nethercote 2017-09-27 20:19:33 +10:00
parent 32f449da79
commit 9e0714dd26
10 changed files with 85 additions and 390 deletions

View File

@ -506,7 +506,6 @@ function ignoreContents(entry)
/(nsTSubstring<T>|nsAC?String)::StripTaggedASCII/,
/(nsTSubstring<T>|nsAC?String)::operator=/,
/nsTAutoStringN<T, N>::nsTAutoStringN/,
/nsTFixedString<T>::nsTFixedString/,
// Similar for some other data structures
/nsCOMArray_base::SetCapacity/,

View File

@ -21,8 +21,6 @@ extern "C" {
SIZE_ALIGN_CHECK(nsString)
SIZE_ALIGN_CHECK(nsCString)
SIZE_ALIGN_CHECK(nsFixedString)
SIZE_ALIGN_CHECK(nsFixedCString)
#define MEMBER_CHECK(Clazz, Member) \
extern "C" void Rust_Test_Member_##Clazz##_##Member(size_t* size, \
@ -35,7 +33,7 @@ SIZE_ALIGN_CHECK(nsFixedCString)
size_t size, align, offset; \
Rust_Test_Member_##Clazz##_##Member(&size, &align, &offset); \
EXPECT_EQ(size, sizeof(mozilla::DeclVal<Hack>().Member)); \
EXPECT_EQ(size, alignof(decltype(mozilla::DeclVal<Hack>().Member))); \
EXPECT_EQ(align, alignof(decltype(mozilla::DeclVal<Hack>().Member))); \
EXPECT_EQ(offset, offsetof(Hack, Member)); \
} \
}; \
@ -51,26 +49,22 @@ MEMBER_CHECK(nsCString, mData)
MEMBER_CHECK(nsCString, mLength)
MEMBER_CHECK(nsCString, mDataFlags)
MEMBER_CHECK(nsCString, mClassFlags)
MEMBER_CHECK(nsFixedString, mFixedCapacity)
MEMBER_CHECK(nsFixedString, mFixedBuf)
MEMBER_CHECK(nsFixedCString, mFixedCapacity)
MEMBER_CHECK(nsFixedCString, mFixedBuf)
extern "C" void Rust_Test_NsStringFlags(uint16_t* f_terminated,
uint16_t* f_voided,
uint16_t* f_shared,
uint16_t* f_owned,
uint16_t* f_fixed,
uint16_t* f_inline,
uint16_t* f_literal,
uint16_t* f_class_fixed,
uint16_t* f_class_inline,
uint16_t* f_class_null_terminated);
TEST(RustNsString, NsStringFlags) {
uint16_t f_terminated, f_voided, f_shared, f_owned, f_fixed, f_literal,
f_class_fixed, f_class_null_terminated;
uint16_t f_terminated, f_voided, f_shared, f_owned, f_inline, f_literal,
f_class_inline, f_class_null_terminated;
Rust_Test_NsStringFlags(&f_terminated,
&f_voided, &f_shared,
&f_owned, &f_fixed,
&f_literal, &f_class_fixed, &f_class_null_terminated);
&f_owned, &f_inline,
&f_literal, &f_class_inline, &f_class_null_terminated);
EXPECT_EQ(f_terminated, uint16_t(nsAString::DataFlags::TERMINATED));
EXPECT_EQ(f_terminated, uint16_t(nsACString::DataFlags::TERMINATED));
EXPECT_EQ(f_voided, uint16_t(nsAString::DataFlags::VOIDED));
@ -79,12 +73,12 @@ TEST(RustNsString, NsStringFlags) {
EXPECT_EQ(f_shared, uint16_t(nsACString::DataFlags::SHARED));
EXPECT_EQ(f_owned, uint16_t(nsAString::DataFlags::OWNED));
EXPECT_EQ(f_owned, uint16_t(nsACString::DataFlags::OWNED));
EXPECT_EQ(f_fixed, uint16_t(nsAString::DataFlags::FIXED));
EXPECT_EQ(f_fixed, uint16_t(nsACString::DataFlags::FIXED));
EXPECT_EQ(f_inline, uint16_t(nsAString::DataFlags::INLINE));
EXPECT_EQ(f_inline, uint16_t(nsACString::DataFlags::INLINE));
EXPECT_EQ(f_literal, uint16_t(nsAString::DataFlags::LITERAL));
EXPECT_EQ(f_literal, uint16_t(nsACString::DataFlags::LITERAL));
EXPECT_EQ(f_class_fixed, uint16_t(nsAString::ClassFlags::FIXED));
EXPECT_EQ(f_class_fixed, uint16_t(nsACString::ClassFlags::FIXED));
EXPECT_EQ(f_class_inline, uint16_t(nsAString::ClassFlags::INLINE));
EXPECT_EQ(f_class_inline, uint16_t(nsACString::ClassFlags::INLINE));
EXPECT_EQ(f_class_null_terminated, uint16_t(nsAString::ClassFlags::NULL_TERMINATED));
EXPECT_EQ(f_class_null_terminated, uint16_t(nsACString::ClassFlags::NULL_TERMINATED));
}
@ -119,14 +113,6 @@ extern "C" void Rust_AssignFromCpp();
TEST(RustNsString, AssignFromCpp) {
Rust_AssignFromCpp();
}
extern "C" void Rust_FixedAssignFromCpp();
TEST(RustNsString, FixedAssignFromCpp) {
Rust_FixedAssignFromCpp();
}
extern "C" void Rust_AutoAssignFromCpp();
TEST(RustNsString, AutoAssignFromCpp) {
Rust_AutoAssignFromCpp();
}
extern "C" void Rust_StringWrite();
TEST(RustNsString, StringWrite) {

View File

@ -1,6 +1,5 @@
#![allow(non_snake_case)]
#[macro_use]
extern crate nsstring;
use std::fmt::Write;
@ -63,38 +62,10 @@ pub extern fn Rust_AssignFromCpp() {
expect_eq!(s, "Hello, World!");
}
#[no_mangle]
pub extern fn Rust_FixedAssignFromCpp() {
let mut cs_buf: [u8; 64] = [0; 64];
let cs_buf_ptr = &cs_buf as *const _ as usize;
let mut s_buf: [u16; 64] = [0; 64];
let s_buf_ptr = &s_buf as *const _ as usize;
let mut cs = nsFixedCString::new(&mut cs_buf);
let mut s = nsFixedString::new(&mut s_buf);
unsafe {
Cpp_AssignFromCpp(&mut *cs, &mut *s);
}
expect_eq!(cs, "Hello, World!");
expect_eq!(s, "Hello, World!");
expect_eq!(cs.as_ptr() as usize, cs_buf_ptr);
expect_eq!(s.as_ptr() as usize, s_buf_ptr);
}
#[no_mangle]
pub extern fn Rust_AutoAssignFromCpp() {
ns_auto_cstring!(cs);
ns_auto_string!(s);
unsafe {
Cpp_AssignFromCpp(&mut *cs, &mut *s);
}
expect_eq!(cs, "Hello, World!");
expect_eq!(s, "Hello, World!");
}
#[no_mangle]
pub extern fn Rust_StringWrite() {
ns_auto_cstring!(cs);
ns_auto_string!(s);
let mut cs = nsCString::new();
let mut s = nsString::new();
write!(s, "a").unwrap();
write!(cs, "a").unwrap();

View File

@ -18,12 +18,16 @@
//! struct field, but passing the borrowed `&{mut,} nsA[C]String` over FFI is
//! safe.
//!
//! Use `nsFixed[C]String` or `ns_auto_[c]string!` for dynamic stack allocated
//! strings which are expected to hold short string values.
//!
//! Use `*{const,mut} nsA[C]String` (`{const,} nsA[C]String*` in C++) for
//! function arguments passed across the rust/C++ language boundary.
//!
//! There is currently no Rust equivalent to nsAuto[C]String. Implementing a
//! type that contains a pointer to an inline buffer is difficult in Rust due
//! to its move semantics, which require that it be safe to move a value by
//! copying its bits. If such a type is genuinely needed at some point,
//! https://bugzilla.mozilla.org/show_bug.cgi?id=1403506#c6 has a sketch of how
//! to emulate it via macros.
//!
//! # String Types
//!
//! ## `nsA[C]String`
@ -94,36 +98,6 @@
//! mutable reference. This struct may also be included in `#[repr(C)]` structs
//! shared with C++.
//!
//! ## `nsFixed[C]String<'a>`
//!
//! This type is a string type with fixed backing storage. It is created with
//! `nsFixed[C]String::new(buffer)`, passing a mutable reference to a buffer as
//! the argument. This buffer will be used as backing storage whenever the
//! resulting string will fit within it, falling back to heap allocations only
//! when the string size exceeds that of the backing buffer.
//!
//! Like `ns[C]String`, this type dereferences to `nsA[C]String` which provides
//! the methods for manipulating the type, and is not `#[repr(C)]`.
//!
//! When passing this type by reference, prefer passing a `&nsA[C]String` or
//! `&mut nsA[C]String`. to passing this type.
//!
//! When passing this type across the language boundary, pass it as `*const
//! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
//! mutable reference. This struct may also be included in `#[repr(C)]`
//! structs shared with C++, although `nsFixed[C]String` objects are uncommon
//! as struct members.
//!
//! ## `ns_auto_[c]string!($name)`
//!
//! This is a helper macro which defines a fixed size, (currently 64 character),
//! backing array on the stack, and defines a local variable with name `$name`
//! which is a `nsFixed[C]String` using this buffer as its backing storage.
//!
//! Usage of this macro is similar to the C++ type `nsAuto[C]String`, but could
//! not be implemented as a basic type due to the differences between rust and
//! C++'s move semantics.
//!
//! ## `ns[C]StringRepr`
//!
//! This crate also provides the type `ns[C]StringRepr` which acts conceptually
@ -151,9 +125,9 @@ use std::str;
use std::u32;
use std::os::raw::c_void;
//////////////////////////////////
// Internal Implemenation Flags //
//////////////////////////////////
///////////////////////////////////
// Internal Implementation Flags //
///////////////////////////////////
mod data_flags {
bitflags! {
@ -165,7 +139,7 @@ mod data_flags {
const VOIDED = 1 << 1, // IsVoid returns true
const SHARED = 1 << 2, // mData points to a heap-allocated, shared buffer
const OWNED = 1 << 3, // mData points to a heap-allocated, raw buffer
const FIXED = 1 << 4, // mData points to a fixed-size writable, dependent buffer
const INLINE = 1 << 4, // mData points to a writable, inline buffer
const LITERAL = 1 << 5, // mData points to a string literal; TERMINATED will also be set
}
}
@ -177,7 +151,7 @@ mod class_flags {
// over FFI safely as a u16.
#[repr(C)]
pub flags ClassFlags : u16 {
const FIXED = 1 << 0, // |this| is of type nsTFixedString
const INLINE = 1 << 0, // |this|'s buffer is inline
const NULL_TERMINATED = 1 << 1, // |this| requires its buffer is null-terminated
}
}
@ -197,7 +171,6 @@ macro_rules! define_string_types {
AString = $AString: ident;
String = $String: ident;
Str = $Str: ident;
FixedString = $FixedString: ident;
StringLike = $StringLike: ident;
StringAdapter = $StringAdapter: ident;
@ -431,12 +404,6 @@ macro_rules! define_string_types {
}
}
impl<'a> cmp::PartialEq<$FixedString<'a>> for $AString {
fn eq(&self, other: &$FixedString<'a>) -> bool {
self.eq(&**other)
}
}
#[repr(C)]
pub struct $Str<'a> {
hdr: $StringRepr,
@ -702,100 +669,6 @@ macro_rules! define_string_types {
}
}
/// A nsFixed[C]String is a string which uses a fixed size mutable
/// backing buffer for storing strings which will fit within that
/// buffer, rather than using heap allocations.
#[repr(C)]
pub struct $FixedString<'a> {
base: $String,
capacity: u32,
buffer: *mut $char_t,
_marker: PhantomData<&'a mut [$char_t]>,
}
impl<'a> $FixedString<'a> {
pub fn new(buf: &'a mut [$char_t]) -> $FixedString<'a> {
let len = buf.len();
assert!(len < (u32::MAX as usize));
let buf_ptr = buf.as_mut_ptr();
$FixedString {
base: $String {
hdr: $StringRepr::new(class_flags::FIXED | class_flags::NULL_TERMINATED),
},
capacity: len as u32,
buffer: buf_ptr,
_marker: PhantomData,
}
}
}
impl<'a> Deref for $FixedString<'a> {
type Target = $AString;
fn deref(&self) -> &$AString {
&self.base
}
}
impl<'a> DerefMut for $FixedString<'a> {
fn deref_mut(&mut self) -> &mut $AString {
&mut self.base
}
}
impl<'a> AsRef<[$char_t]> for $FixedString<'a> {
fn as_ref(&self) -> &[$char_t] {
&self
}
}
impl<'a> fmt::Write for $FixedString<'a> {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
$AString::write_str(self, s)
}
}
impl<'a> fmt::Display for $FixedString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
<$AString as fmt::Display>::fmt(self, f)
}
}
impl<'a> fmt::Debug for $FixedString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
<$AString as fmt::Debug>::fmt(self, f)
}
}
impl<'a> cmp::PartialEq for $FixedString<'a> {
fn eq(&self, other: &$FixedString<'a>) -> bool {
$AString::eq(self, other)
}
}
impl<'a> cmp::PartialEq<[$char_t]> for $FixedString<'a> {
fn eq(&self, other: &[$char_t]) -> bool {
$AString::eq(self, other)
}
}
impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $FixedString<'a> {
fn eq(&self, other: &&'b [$char_t]) -> bool {
$AString::eq(self, *other)
}
}
impl<'a> cmp::PartialEq<str> for $FixedString<'a> {
fn eq(&self, other: &str) -> bool {
$AString::eq(self, other)
}
}
impl<'a, 'b> cmp::PartialEq<&'b str> for $FixedString<'a> {
fn eq(&self, other: &&'b str) -> bool {
$AString::eq(self, *other)
}
}
/// An adapter type to allow for passing both types which coerce to
/// &[$char_type], and &$AString to a function, while still performing
/// optimized operations when passed the $AString.
@ -865,12 +738,6 @@ macro_rules! define_string_types {
}
}
impl<'a> $StringLike for $FixedString<'a> {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Abstract(self)
}
}
impl $StringLike for [$char_t] {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Borrowed($Str::from(self))
@ -901,7 +768,6 @@ define_string_types! {
AString = nsACString;
String = nsCString;
Str = nsCStr;
FixedString = nsFixedCString;
StringLike = nsCStringLike;
StringAdapter = nsCStringAdapter;
@ -1025,14 +891,6 @@ impl nsCStringLike for Box<str> {
}
}
#[macro_export]
macro_rules! ns_auto_cstring {
($name:ident) => {
let mut buf: [u8; 64] = [0; 64];
let mut $name = $crate::nsFixedCString::new(&mut buf);
}
}
///////////////////////////////////////////
// Bindings for nsString (u16 char type) //
///////////////////////////////////////////
@ -1043,7 +901,6 @@ define_string_types! {
AString = nsAString;
String = nsString;
Str = nsStr;
FixedString = nsFixedString;
StringLike = nsStringLike;
StringAdapter = nsStringAdapter;
@ -1125,14 +982,6 @@ impl cmp::PartialEq<str> for nsAString {
}
}
#[macro_export]
macro_rules! ns_auto_string {
($name:ident) => {
let mut buf: [u16; 64] = [0; 64];
let mut $name = $crate::nsFixedString::new(&mut buf);
}
}
#[cfg(not(debug_assertions))]
#[allow(non_snake_case)]
unsafe fn Gecko_IncrementStringAdoptCount(_: *mut c_void) {}
@ -1183,8 +1032,6 @@ pub mod test_helpers {
//! gtest code.
use super::{
nsFixedCString,
nsFixedString,
nsCString,
nsString,
nsCStr,
@ -1230,8 +1077,6 @@ pub mod test_helpers {
Rust_Test_ReprSizeAlign_nsString);
size_align_check!(nsCStringRepr, nsCString, nsCStr<'static>,
Rust_Test_ReprSizeAlign_nsCString);
size_align_check!(nsFixedString<'static>, Rust_Test_ReprSizeAlign_nsFixedString);
size_align_check!(nsFixedCString<'static>, Rust_Test_ReprSizeAlign_nsFixedCString);
/// Generates a $[no_mangle] extern "C" function which returns the size,
/// alignment and offset in the parent struct of a given member, with the
@ -1312,10 +1157,6 @@ pub mod test_helpers {
dataflags, Rust_Test_Member_nsCString_mDataFlags);
member_check!(nsCStringRepr, nsCString, nsCStr<'static>,
classflags, Rust_Test_Member_nsCString_mClassFlags);
member_check!(nsFixedString<'static>, capacity, Rust_Test_Member_nsFixedString_mFixedCapacity);
member_check!(nsFixedString<'static>, buffer, Rust_Test_Member_nsFixedString_mFixedBuf);
member_check!(nsFixedCString<'static>, capacity, Rust_Test_Member_nsFixedCString_mFixedCapacity);
member_check!(nsFixedCString<'static>, buffer, Rust_Test_Member_nsFixedCString_mFixedBuf);
#[no_mangle]
#[allow(non_snake_case)]
@ -1323,18 +1164,18 @@ pub mod test_helpers {
f_voided: *mut u16,
f_shared: *mut u16,
f_owned: *mut u16,
f_fixed: *mut u16,
f_inline: *mut u16,
f_literal: *mut u16,
f_class_fixed: *mut u16,
f_class_inline: *mut u16,
f_class_null_terminated: *mut u16) {
unsafe {
*f_terminated = data_flags::TERMINATED.bits();
*f_voided = data_flags::VOIDED.bits();
*f_shared = data_flags::SHARED.bits();
*f_owned = data_flags::OWNED.bits();
*f_fixed = data_flags::FIXED.bits();
*f_inline = data_flags::INLINE.bits();
*f_literal = data_flags::LITERAL.bits();
*f_class_fixed = class_flags::FIXED.bits();
*f_class_inline = class_flags::INLINE.bits();
*f_class_null_terminated = class_flags::NULL_TERMINATED.bits();
}
}

View File

@ -40,30 +40,30 @@ enum class StringDataFlags : uint16_t
//
// Some comments about the string data flags:
//
// SHARED, OWNED, and FIXED are all mutually exlusive. They
// SHARED, OWNED, and INLINE are all mutually exlusive. They
// indicate the allocation type of mData. If none of these flags
// are set, then the string buffer is dependent.
//
// SHARED, OWNED, or FIXED imply TERMINATED. This is because
// SHARED, OWNED, or INLINE imply TERMINATED. This is because
// the string classes always allocate null-terminated buffers, and
// non-terminated substrings are always dependent.
//
// VOIDED implies TERMINATED, and moreover it implies that mData
// points to char_traits::sEmptyBuffer. Therefore, VOIDED is
// mutually exclusive with SHARED, OWNED, and FIXED.
// mutually exclusive with SHARED, OWNED, and INLINE.
TERMINATED = 1 << 0, // IsTerminated returns true
VOIDED = 1 << 1, // IsVoid returns true
SHARED = 1 << 2, // mData points to a heap-allocated, shared buffer
OWNED = 1 << 3, // mData points to a heap-allocated, raw buffer
FIXED = 1 << 4, // mData points to a fixed-size writable, dependent buffer
INLINE = 1 << 4, // mData points to a writable, inline buffer
LITERAL = 1 << 5 // mData points to a string literal; DataFlags::TERMINATED will also be set
};
// bits for mClassFlags
enum class StringClassFlags : uint16_t
{
FIXED = 1 << 0, // |this| is of type nsTFixedString
INLINE = 1 << 0, // |this|'s buffer is inline
NULL_TERMINATED = 1 << 1 // |this| requires its buffer is null-terminated
};

View File

@ -34,7 +34,6 @@ template <typename T> class nsTPromiseFlatString;
template <typename T> class nsTStringComparator;
template <typename T> class nsTDefaultStringComparator;
template <typename T> class nsTLiteralString;
template <typename T> class nsTFixedString;
// We define this version without a size param instead of providing a
// default value for N so that so there is a default typename that doesn't
@ -55,7 +54,6 @@ using nsPromiseFlatString = nsTPromiseFlatString<char16_t>;
using nsStringComparator = nsTStringComparator<char16_t>;
using nsDefaultStringComparator = nsTDefaultStringComparator<char16_t>;
using nsLiteralString = nsTLiteralString<char16_t>;
using nsFixedString = nsTFixedString<char16_t>;
// Single-byte (char) string types.
@ -70,6 +68,5 @@ using nsPromiseFlatCString = nsTPromiseFlatString<char>;
using nsCStringComparator = nsTStringComparator<char>;
using nsDefaultCStringComparator = nsTDefaultStringComparator<char>;
using nsLiteralCString = nsTLiteralString<char>;
using nsFixedCString = nsTFixedString<char>;
#endif /* !defined(nsStringFwd_h) */

View File

@ -525,13 +525,24 @@ protected:
//extern template class nsTString<char>;
//extern template class nsTString<char16_t>;
template <typename T>
class nsTFixedString : public nsTString<T>
/**
* nsTAutoStringN
*
* Subclass of nsTString that adds support for stack-based string
* allocation. It is normally not a good idea to use this class on the
* heap, because it will allocate space which may be wasted if the string
* it contains is significantly smaller or any larger than 64 characters.
*
* NAMES:
* nsAutoStringN / nsTAutoString for wide characters
* nsAutoCStringN / nsTAutoCString for narrow characters
*/
template<typename T, size_t N>
class MOZ_NON_MEMMOVABLE nsTAutoStringN : public nsTString<T>
{
public:
typedef nsTFixedString<T> self_type;
typedef nsTFixedString<T> fixed_string_type;
typedef nsTAutoStringN<T, N> self_type;
typedef nsTString<T> base_string_type;
typedef typename base_string_type::string_type string_type;
@ -548,108 +559,6 @@ public:
using typename base_string_type::IsChar;
using typename base_string_type::IsChar16;
public:
/**
* @param aData
* fixed-size buffer to be used by the string (the contents of
* this buffer may be modified by the string)
* @param aStorageSize
* the size of the fixed buffer
* @param aLength (optional)
* the length of the string already contained in the buffer
*/
nsTFixedString(char_type* aData, size_type aStorageSize)
: string_type(aData, uint32_t(char_traits::length(aData)),
DataFlags::TERMINATED | DataFlags::FIXED,
ClassFlags::FIXED)
, mFixedCapacity(aStorageSize - 1)
, mFixedBuf(aData)
{
}
nsTFixedString(char_type* aData, size_type aStorageSize,
size_type aLength)
: string_type(aData, aLength, DataFlags::TERMINATED | DataFlags::FIXED,
ClassFlags::FIXED)
, mFixedCapacity(aStorageSize - 1)
, mFixedBuf(aData)
{
// null-terminate
mFixedBuf[aLength] = char_type(0);
}
// |operator=| does not inherit, so we must define our own
self_type& operator=(char_type aChar)
{
this->Assign(aChar);
return *this;
}
self_type& operator=(const char_type* aData)
{
this->Assign(aData);
return *this;
}
self_type& operator=(const substring_type& aStr)
{
this->Assign(aStr);
return *this;
}
self_type& operator=(const substring_tuple_type& aTuple)
{
this->Assign(aTuple);
return *this;
}
protected:
friend class nsTSubstring<T>;
size_type mFixedCapacity;
char_type* mFixedBuf;
};
extern template class nsTFixedString<char>;
extern template class nsTFixedString<char>;
/**
* nsTAutoStringN
*
* Subclass of nsTString that adds support for stack-based string
* allocation. It is normally not a good idea to use this class on the
* heap, because it will allocate space which may be wasted if the string
* it contains is significantly smaller or any larger than 64 characters.
*
* NAMES:
* nsAutoStringN / nsTAutoString for wide characters
* nsAutoCStringN / nsTAutoCString for narrow characters
*/
template<typename T, size_t N>
class MOZ_NON_MEMMOVABLE nsTAutoStringN : public nsTFixedString<T>
{
public:
typedef nsTAutoStringN<T, N> self_type;
#ifdef __clang__
// bindgen w/ clang 3.9 at least chokes on a typedef, but using is okay.
using typename nsTFixedString<T>::fixed_string_type;
#else
// On the other hand msvc chokes on the using statement. It seems others
// don't care either way so we lump them in here.
typedef typename nsTFixedString<T>::fixed_string_type fixed_string_type;
#endif
typedef typename fixed_string_type::char_type char_type;
typedef typename fixed_string_type::char_traits char_traits;
typedef typename fixed_string_type::substring_type substring_type;
typedef typename fixed_string_type::size_type size_type;
typedef typename fixed_string_type::substring_tuple_type substring_tuple_type;
using typename fixed_string_type::IsChar;
using typename fixed_string_type::IsChar16;
public:
/**
@ -657,20 +566,24 @@ public:
*/
nsTAutoStringN()
: fixed_string_type(mStorage, N, 0)
: string_type(mStorage, 0, DataFlags::TERMINATED | DataFlags::INLINE,
ClassFlags::INLINE)
, mInlineCapacity(N - 1)
{
// null-terminate
mStorage[0] = char_type(0);
}
explicit
nsTAutoStringN(char_type aChar)
: fixed_string_type(mStorage, N, 0)
: self_type()
{
this->Assign(aChar);
}
explicit
nsTAutoStringN(const char_type* aData, size_type aLength = size_type(-1))
: fixed_string_type(mStorage, N, 0)
: self_type()
{
this->Assign(aData, aLength);
}
@ -679,26 +592,26 @@ public:
template <typename EnableIfChar16 = IsChar16>
explicit
nsTAutoStringN(char16ptr_t aData, size_type aLength = size_type(-1))
: nsTAutoStringN(static_cast<const char16_t*>(aData), aLength)
: self_type(static_cast<const char16_t*>(aData), aLength)
{
}
#endif
nsTAutoStringN(const self_type& aStr)
: fixed_string_type(mStorage, N, 0)
: self_type()
{
this->Assign(aStr);
}
explicit
nsTAutoStringN(const substring_type& aStr)
: fixed_string_type(mStorage, N, 0)
: self_type()
{
this->Assign(aStr);
}
MOZ_IMPLICIT nsTAutoStringN(const substring_tuple_type& aTuple)
: fixed_string_type(mStorage, N, 0)
: self_type()
{
this->Assign(aTuple);
}
@ -740,8 +653,12 @@ public:
static const size_t kStorageSize = N;
private:
protected:
friend class nsTSubstring<T>;
size_type mInlineCapacity;
private:
char_type mStorage[N];
};

View File

@ -37,13 +37,13 @@ nsTSubstring<T>::nsTSubstring(char_type* aData, size_type aLength,
#endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
/**
* helper function for down-casting a nsTSubstring to a nsTFixedString.
* helper function for down-casting a nsTSubstring to an nsTAutoString.
*/
template <typename T>
inline const nsTFixedString<T>*
AsFixedString(const nsTSubstring<T>* aStr)
inline const nsTAutoString<T>*
AsAutoString(const nsTSubstring<T>* aStr)
{
return static_cast<const nsTFixedString<T>*>(aStr);
return static_cast<const nsTAutoString<T>*>(aStr);
}
/**
@ -122,7 +122,7 @@ nsTSubstring<T>::MutatePrep(size_type aCapacity, char_type** aOldData,
//
// (1) we have a shared buffer (this->mDataFlags & DataFlags::SHARED)
// (2) we have an owned buffer (this->mDataFlags & DataFlags::OWNED)
// (3) we have a fixed buffer (this->mDataFlags & DataFlags::FIXED)
// (3) we have an inline buffer (this->mDataFlags & DataFlags::INLINE)
// (4) we have a readonly buffer
//
// requiring that we in some cases preserve the data before creating
@ -150,16 +150,16 @@ nsTSubstring<T>::MutatePrep(size_type aCapacity, char_type** aOldData,
char_type* newData;
DataFlags newDataFlags;
// if we have a fixed buffer of sufficient size, then use it. this helps
// avoid heap allocations.
if ((this->mClassFlags & ClassFlags::FIXED) &&
(aCapacity < AsFixedString(this)->mFixedCapacity)) {
newData = AsFixedString(this)->mFixedBuf;
newDataFlags = DataFlags::TERMINATED | DataFlags::FIXED;
// If this is an nsTAutoStringN whose inline buffer is sufficiently large,
// then use it. This helps avoid heap allocations.
if ((this->mClassFlags & ClassFlags::INLINE) &&
(aCapacity < AsAutoString(this)->mInlineCapacity)) {
newData = (char_type*)AsAutoString(this)->mStorage;
newDataFlags = DataFlags::TERMINATED | DataFlags::INLINE;
} else {
// if we reach here then, we must allocate a new buffer. we cannot
// make use of our DataFlags::OWNED or DataFlags::FIXED buffers because they are not
// large enough.
// make use of our DataFlags::OWNED or DataFlags::INLINE buffers because
// they are not large enough.
nsStringBuffer* newHdr =
nsStringBuffer::Alloc(storageSize).take();
@ -283,8 +283,8 @@ nsTSubstring<T>::Capacity() const
} else {
capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
}
} else if (this->mDataFlags & DataFlags::FIXED) {
capacity = AsFixedString(this)->mFixedCapacity;
} else if (this->mDataFlags & DataFlags::INLINE) {
capacity = AsAutoString(this)->mInlineCapacity;
} else if (this->mDataFlags & DataFlags::OWNED) {
// we don't store the capacity of an adopted buffer because that would
// require an additional member field. the best we can do is base the
@ -303,7 +303,7 @@ bool
nsTSubstring<T>::EnsureMutable(size_type aNewLen)
{
if (aNewLen == size_type(-1) || aNewLen == this->mLength) {
if (this->mDataFlags & (DataFlags::FIXED | DataFlags::OWNED)) {
if (this->mDataFlags & (DataFlags::INLINE | DataFlags::OWNED)) {
return true;
}
if ((this->mDataFlags & DataFlags::SHARED) &&
@ -1177,10 +1177,10 @@ nsTSubstring<T>::SizeOfExcludingThisIfUnshared(
// If we reach here, exactly one of the following must be true:
// - DataFlags::VOIDED is set, and this->mData points to sEmptyBuffer;
// - DataFlags::FIXED is set, and this->mData points to a buffer within a string
// object (e.g. nsAutoString);
// - None of DataFlags::SHARED, DataFlags::OWNED, DataFlags::FIXED is set, and this->mData points to a buffer
// owned by something else.
// - DataFlags::INLINE is set, and this->mData points to a buffer within a
// string object (e.g. nsAutoString);
// - None of DataFlags::SHARED, DataFlags::OWNED, DataFlags::INLINE is set,
// and this->mData points to a buffer owned by something else.
//
// In all three cases, we don't measure it.
return 0;

View File

@ -27,8 +27,6 @@ template class nsTDependentSubstring<char16_t>;
// Note: nsTString is skipped as it's implicitly instantiated by derived
// classes.
template class nsTFixedString<char>;
template class nsTFixedString<char16_t>;
template class nsTAutoStringN<char, 64>;
template class nsTAutoStringN<char16_t, 64>;

View File

@ -426,20 +426,6 @@ TEST(Strings, equals_ic)
EXPECT_FALSE(s.LowerCaseEqualsLiteral("view-source"));
}
TEST(Strings, fixed_string)
{
char buf[256] = "hello world";
nsFixedCString s(buf, sizeof(buf));
EXPECT_EQ(s.Length(), strlen(buf));
EXPECT_STREQ(s.get(), buf);
s.Assign("foopy doopy doo");
EXPECT_EQ(s.get(), buf);
}
TEST(Strings, concat)
{
nsCString bar("bar");