michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * A class which represents a fragment of text (eg inside a text michael@0: * node); if only codepoints below 256 are used, the text is stored as michael@0: * a char*; otherwise the text is stored as a char16_t* michael@0: */ michael@0: michael@0: #ifndef nsTextFragment_h___ michael@0: #define nsTextFragment_h___ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "nsString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsISupportsImpl.h" michael@0: michael@0: class nsString; michael@0: class nsCString; michael@0: michael@0: // XXX should this normalize the code to keep a \u0000 at the end? michael@0: michael@0: // XXX nsTextFragmentPool? michael@0: michael@0: /** michael@0: * A fragment of text. If mIs2b is 1 then the m2b pointer is valid michael@0: * otherwise the m1b pointer is valid. If m1b is used then each byte michael@0: * of data represents a single ucs2 character with the high byte being michael@0: * zero. michael@0: * michael@0: * This class does not have a virtual destructor therefore it is not michael@0: * meant to be subclassed. michael@0: */ michael@0: class nsTextFragment MOZ_FINAL { michael@0: public: michael@0: static nsresult Init(); michael@0: static void Shutdown(); michael@0: michael@0: /** michael@0: * Default constructor. Initialize the fragment to be empty. michael@0: */ michael@0: nsTextFragment() michael@0: : m1b(nullptr), mAllBits(0) michael@0: { michael@0: MOZ_COUNT_CTOR(nsTextFragment); michael@0: NS_ASSERTION(sizeof(FragmentBits) == 4, "Bad field packing!"); michael@0: } michael@0: michael@0: ~nsTextFragment(); michael@0: michael@0: /** michael@0: * Change the contents of this fragment to be a copy of the michael@0: * the argument fragment, or to "" if unable to allocate enough memory. michael@0: */ michael@0: nsTextFragment& operator=(const nsTextFragment& aOther); michael@0: michael@0: /** michael@0: * Return true if this fragment is represented by char16_t data michael@0: */ michael@0: bool Is2b() const michael@0: { michael@0: return mState.mIs2b; michael@0: } michael@0: michael@0: /** michael@0: * Return true if this fragment contains Bidi text michael@0: * For performance reasons this flag is only set if explicitely requested (by michael@0: * setting the aUpdateBidi argument on SetTo or Append to true). michael@0: */ michael@0: bool IsBidi() const michael@0: { michael@0: return mState.mIsBidi; michael@0: } michael@0: michael@0: /** michael@0: * Get a pointer to constant char16_t data. michael@0: */ michael@0: const char16_t *Get2b() const michael@0: { michael@0: NS_ASSERTION(Is2b(), "not 2b text"); michael@0: return m2b; michael@0: } michael@0: michael@0: /** michael@0: * Get a pointer to constant char data. michael@0: */ michael@0: const char *Get1b() const michael@0: { michael@0: NS_ASSERTION(!Is2b(), "not 1b text"); michael@0: return (const char *)m1b; michael@0: } michael@0: michael@0: /** michael@0: * Get the length of the fragment. The length is the number of logical michael@0: * characters, not the number of bytes to store the characters. michael@0: */ michael@0: uint32_t GetLength() const michael@0: { michael@0: return mState.mLength; michael@0: } michael@0: michael@0: bool CanGrowBy(size_t n) const michael@0: { michael@0: return n < (1 << 29) && mState.mLength + n < (1 << 29); michael@0: } michael@0: michael@0: /** michael@0: * Change the contents of this fragment to be a copy of the given michael@0: * buffer. If aUpdateBidi is true, contents of the fragment will be scanned, michael@0: * and mState.mIsBidi will be turned on if it includes any Bidi characters. michael@0: */ michael@0: bool SetTo(const char16_t* aBuffer, int32_t aLength, bool aUpdateBidi); michael@0: michael@0: /** michael@0: * Append aData to the end of this fragment. If aUpdateBidi is true, contents michael@0: * of the fragment will be scanned, and mState.mIsBidi will be turned on if michael@0: * it includes any Bidi characters. michael@0: */ michael@0: bool Append(const char16_t* aBuffer, uint32_t aLength, bool aUpdateBidi); michael@0: michael@0: /** michael@0: * Append the contents of this string fragment to aString michael@0: */ michael@0: void AppendTo(nsAString& aString) const { michael@0: if (!AppendTo(aString, mozilla::fallible_t())) { michael@0: NS_ABORT_OOM(GetLength()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Append the contents of this string fragment to aString michael@0: * @return false if an out of memory condition is detected, true otherwise michael@0: */ michael@0: bool AppendTo(nsAString& aString, michael@0: const mozilla::fallible_t&) const NS_WARN_UNUSED_RESULT { michael@0: if (mState.mIs2b) { michael@0: bool ok = aString.Append(m2b, mState.mLength, mozilla::fallible_t()); michael@0: if (!ok) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } else { michael@0: return AppendASCIItoUTF16(Substring(m1b, mState.mLength), aString, michael@0: mozilla::fallible_t()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Append a substring of the contents of this string fragment to aString. michael@0: * @param aOffset where to start the substring in this text fragment michael@0: * @param aLength the length of the substring michael@0: */ michael@0: void AppendTo(nsAString& aString, int32_t aOffset, int32_t aLength) const { michael@0: if (!AppendTo(aString, aOffset, aLength, mozilla::fallible_t())) { michael@0: NS_ABORT_OOM(aLength); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Append a substring of the contents of this string fragment to aString. michael@0: * @param aString the string in which to append michael@0: * @param aOffset where to start the substring in this text fragment michael@0: * @param aLength the length of the substring michael@0: * @return false if an out of memory condition is detected, true otherwise michael@0: */ michael@0: bool AppendTo(nsAString& aString, int32_t aOffset, int32_t aLength, michael@0: const mozilla::fallible_t&) const NS_WARN_UNUSED_RESULT michael@0: { michael@0: if (mState.mIs2b) { michael@0: bool ok = aString.Append(m2b + aOffset, aLength, mozilla::fallible_t()); michael@0: if (!ok) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } else { michael@0: return AppendASCIItoUTF16(Substring(m1b + aOffset, aLength), aString, michael@0: mozilla::fallible_t()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Make a copy of the fragments contents starting at offset for michael@0: * count characters. The offset and count will be adjusted to michael@0: * lie within the fragments data. The fragments data is converted if michael@0: * necessary. michael@0: */ michael@0: void CopyTo(char16_t *aDest, int32_t aOffset, int32_t aCount); michael@0: michael@0: /** michael@0: * Return the character in the text-fragment at the given michael@0: * index. This always returns a char16_t. michael@0: */ michael@0: char16_t CharAt(int32_t aIndex) const michael@0: { michael@0: NS_ASSERTION(uint32_t(aIndex) < mState.mLength, "bad index"); michael@0: return mState.mIs2b ? m2b[aIndex] : static_cast(m1b[aIndex]); michael@0: } michael@0: michael@0: struct FragmentBits { michael@0: // uint32_t to ensure that the values are unsigned, because we michael@0: // want 0/1, not 0/-1! michael@0: // Making these bool causes Windows to not actually pack them, michael@0: // which causes crashes because we assume this structure is no more than michael@0: // 32 bits! michael@0: uint32_t mInHeap : 1; michael@0: uint32_t mIs2b : 1; michael@0: uint32_t mIsBidi : 1; michael@0: uint32_t mLength : 29; michael@0: }; michael@0: michael@0: size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: private: michael@0: void ReleaseText(); michael@0: michael@0: /** michael@0: * Scan the contents of the fragment and turn on mState.mIsBidi if it michael@0: * includes any Bidi characters. michael@0: */ michael@0: void UpdateBidiFlag(const char16_t* aBuffer, uint32_t aLength); michael@0: michael@0: union { michael@0: char16_t *m2b; michael@0: const char *m1b; // This is const since it can point to shared data michael@0: }; michael@0: michael@0: union { michael@0: uint32_t mAllBits; michael@0: FragmentBits mState; michael@0: }; michael@0: }; michael@0: michael@0: #endif /* nsTextFragment_h___ */ michael@0: