Bug 403589. Fuse memory allocations in gfxTextRun. r=pavlov
authorroc+@cs.cmu.edu
Thu, 15 Nov 2007 17:43:47 -0800
changeset 8081 1a966f689d12f0c076b8a8b58b4c11e54ac84a2b
parent 8080 b0f1a88b2270d13c1b7bb4f2587dc9c42284f5a4
child 8082 317c01d10b837b58a6c28a83d239d56adee27a15
push idunknown
push userunknown
push dateunknown
reviewerspavlov
bugs403589
milestone1.9b2pre
Bug 403589. Fuse memory allocations in gfxTextRun. r=pavlov
gfx/thebes/public/gfxFont.h
gfx/thebes/src/gfxAtsuiFonts.cpp
gfx/thebes/src/gfxFont.cpp
gfx/thebes/src/gfxTextRunWordCache.cpp
layout/generic/nsTextRunTransformations.cpp
layout/generic/nsTextRunTransformations.h
--- a/gfx/thebes/public/gfxFont.h
+++ b/gfx/thebes/public/gfxFont.h
@@ -665,16 +665,18 @@ public:
  * 
  * gfxTextRun methods that measure or draw substrings will associate all the
  * glyphs in a cluster with the first character of the cluster; if that character
  * is in the substring, the glyphs will be measured or drawn, otherwise they
  * won't.
  */
 class THEBES_API gfxTextRun {
 public:
+    // Override operator delete because we used custom allocation
+    void operator delete(void* aPtr);
     virtual ~gfxTextRun();
 
     typedef gfxFont::RunMetrics Metrics;
 
     // Public textrun API for general use
 
     PRBool IsClusterStart(PRUint32 aPos) {
         NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
@@ -942,22 +944,20 @@ public:
             ? static_cast<const void *>(mText.mSingle + aIndex)
             : static_cast<const void *>(mText.mDouble + aIndex);
     }
     const PRUnichar GetChar(PRUint32 i) const
     { return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? mText.mSingle[i] : mText.mDouble[i]; }
     PRUint32 GetHashCode() const { return mHashCode; }
     void SetHashCode(PRUint32 aHash) { mHashCode = aHash; }
 
-    // The caller is responsible for initializing our glyphs after construction.
-    // Initially all glyphs are such that GetCharacterGlyphs()[i].IsMissing() is true.
-    // If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
-    // textrun will copy it.
-    gfxTextRun(const gfxTextRunFactory::Parameters *aParams, const void *aText,
-    		       PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags);
+    // Call this, don't call "new gfxTextRun" directly. This does custom
+    // allocation and initialization
+    static gfxTextRun *Create(const gfxTextRunFactory::Parameters *aParams,
+        const void *aText, PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags);
 
     // Clone this textrun, according to the given parameters. This textrun's
     // glyph data is copied, so the text and length must be the same as this
     // textrun's. If there's a problem, return null. Actual linebreaks will
     // be set as per aParams; there will be no potential linebreaks.
     // If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
     // textrun will copy it.
     virtual gfxTextRun *Clone(const gfxTextRunFactory::Parameters *aParams, const void *aText,
@@ -1246,16 +1246,30 @@ public:
         PRPackedBool mPartIsEndOfLigature;
     };
 
 #ifdef DEBUG
     // number of entries referencing this textrun in the gfxTextRunWordCache
     PRUint32 mCachedWords;
 #endif
 
+protected:
+    // Allocates extra space for the CompressedGlyph array and the text
+    // (if needed)
+    void *operator new(size_t aSize, PRUint32 aLength, PRUint32 aFlags);
+
+    /**
+     * Initializes the textrun to blank.
+     * @param aObjectSize the size of the object; this lets us fine
+     * where our CompressedGlyph array and string have been allocated
+     */
+    gfxTextRun(const gfxTextRunFactory::Parameters *aParams, const void *aText,
+               PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags,
+               PRUint32 aObjectSize);
+
 private:
     // **** general helpers **** 
 
     // Allocate aCount DetailedGlyphs for the given index
     DetailedGlyph *AllocateDetailedGlyphs(PRUint32 aCharIndex, PRUint32 aCount);
 
     // Spacing for characters outside the range aSpacingStart/aSpacingEnd
     // is assumed to be zero; such characters are not passed to aProvider.
@@ -1298,25 +1312,29 @@ private:
                                  Metrics *aMetrics);
 
     // **** drawing helper ****
     void DrawGlyphs(gfxFont *aFont, gfxContext *aContext, PRBool aDrawToPath,
                     gfxPoint *aPt, PRUint32 aStart, PRUint32 aEnd,
                     PropertyProvider *aProvider,
                     PRUint32 aSpacingStart, PRUint32 aSpacingEnd);
 
-    // All our glyph data is in logical order, not visual
-    nsAutoArrayPtr<CompressedGlyph>                mCharacterGlyphs;
+    // All our glyph data is in logical order, not visual.
+    // mCharacterGlyphs is allocated fused with this object. We need a pointer
+    // to it because gfxTextRun subclasses exist with extra fields, so we don't
+    // know where it starts without a virtual method call or an explicit pointer.
+    CompressedGlyph*                               mCharacterGlyphs;
     nsAutoArrayPtr<nsAutoArrayPtr<DetailedGlyph> > mDetailedGlyphs; // only non-null if needed
     // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
     // for smaller size especially in the super-common one-glyphrun case
     nsAutoTArray<GlyphRun,1>                       mGlyphRuns;
     // When TEXT_IS_8BIT is set, we use mSingle, otherwise we use mDouble.
     // When TEXT_IS_PERSISTENT is set, we don't own the text, otherwise we
-    // own the text and should delete it when we go away.
+    // own the text. When we own the text, it's allocated fused with this
+    // object, so it need not be deleted.
     // This text is not null-terminated.
     union {
         const PRUint8   *mSingle;
         const PRUnichar *mDouble;
     } mText;
     void             *mUserData;
     gfxFontGroup     *mFontGroup; // addrefed
     gfxSkipChars      mSkipChars;
--- a/gfx/thebes/src/gfxAtsuiFonts.cpp
+++ b/gfx/thebes/src/gfxAtsuiFonts.cpp
@@ -575,17 +575,17 @@ gfxAtsuiFontGroup::GuessMaximumStringLen
  * If the font size is incredibly huge and/or clusters are very large, this
  * could mean that we actually put more than 'maxLen' characters in a chunk.
  */
 
 gfxTextRun *
 gfxAtsuiFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
                                const Parameters *aParams, PRUint32 aFlags)
 {
-    gfxTextRun *textRun = new gfxTextRun(aParams, aString, aLength, this, aFlags);
+    gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
     if (!textRun)
         return nsnull;
 
     textRun->RecordSurrogates(aString);
     SetupClusterBoundaries(textRun, aString);
 
     PRUint32 maxLen;
     nsAutoString utf16;
@@ -616,17 +616,17 @@ gfxAtsuiFontGroup::MakeTextRun(const PRU
     return textRun;
 }
 
 gfxTextRun *
 gfxAtsuiFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
                                const Parameters *aParams, PRUint32 aFlags)
 {
     NS_ASSERTION(aFlags & TEXT_IS_8BIT, "should be marked 8bit");
-    gfxTextRun *textRun = new gfxTextRun(aParams, aString, aLength, this, aFlags);
+    gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
     if (!textRun)
         return nsnull;
 
     PRUint32 maxLen;
     nsAutoString utf16;
     for (maxLen = GuessMaximumStringLength(); maxLen > 0; maxLen /= 2) {
         PRUint32 start = 0;
         while (start < aLength) {
--- a/gfx/thebes/src/gfxFont.cpp
+++ b/gfx/thebes/src/gfxFont.cpp
@@ -822,28 +822,28 @@ gfxFontGroup::FindGenericFontFromStyle(F
         }
     }
 }
 
 gfxTextRun *
 gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, PRUint32 aFlags)
 {
     aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
-    return new gfxTextRun(aParams, nsnull, 0, this, aFlags);
+    return gfxTextRun::Create(aParams, nsnull, 0, this, aFlags);
 }
 
 gfxTextRun *
 gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, PRUint32 aFlags)
 {
     aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
     static const PRUint8 space = ' ';
 
     nsAutoPtr<gfxTextRun> textRun;
-    textRun = new gfxTextRun(aParams, &space, 1, this, aFlags);
-    if (!textRun || !textRun->GetCharacterGlyphs())
+    textRun = gfxTextRun::Create(aParams, &space, 1, this, aFlags);
+    if (!textRun)
         return nsnull;
 
     gfxFont *font = GetFontAt(0);
     textRun->SetSpaceGlyph(font, aParams->mContext, 0);
     // Note that the gfxGlyphExtents glyph bounds storage for the font will
     // always contain an entry for the font's space glyph, so we don't have
     // to call FetchGlyphExtents here.
     return textRun.forget();
@@ -933,91 +933,100 @@ AccountStorageForTextRun(gfxTextRun *aTe
       bytesPerChar += (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) ? 1 : 2;
     }
     PRInt32 bytes = sizeof(gfxTextRun) + aTextRun->GetLength()*bytesPerChar;
     gTextRunStorage += bytes*aSign;
     gTextRunStorageHighWaterMark = PR_MAX(gTextRunStorageHighWaterMark, gTextRunStorage);
 }
 #endif
 
+gfxTextRun *
+gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams, const void *aText,
+                   PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags)
+{
+    return new (aLength, aFlags)
+        gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags, sizeof(gfxTextRun));
+}
+
+void *
+gfxTextRun::operator new(size_t aSize, PRUint32 aLength, PRUint32 aFlags)
+{
+    NS_ASSERTION(aSize % sizeof(CompressedGlyph) == 0, "Alignment broken!");
+    aSize += sizeof(CompressedGlyph)*aLength;
+    if (!(aFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
+        NS_ASSERTION(aSize % 2 == 0, "Alignment broken!");
+        aSize += ((aFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? 1 : 2)*aLength;
+    }
+
+    return new PRUint8[aSize];
+}
+
+void gfxTextRun::operator delete(void *p)
+{
+    delete[] static_cast<PRUint8*>(p);
+}
+
 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams, const void *aText,
-                       PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags)
+                       PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags,
+                       PRUint32 aObjectSize)
   : mUserData(aParams->mUserData),
     mFontGroup(aFontGroup),
     mAppUnitsPerDevUnit(aParams->mAppUnitsPerDevUnit),
     mFlags(aFlags), mCharacterCount(aLength), mHashCode(0)
 {
     NS_ASSERTION(mAppUnitsPerDevUnit != 0, "Invalid app unit scale");
     MOZ_COUNT_CTOR(gfxTextRun);
     NS_ADDREF(mFontGroup);
     if (aParams->mSkipChars) {
         mSkipChars.TakeFrom(aParams->mSkipChars);
     }
-    if (aLength > 0) {
-        mCharacterGlyphs = new CompressedGlyph[aLength];
-        if (mCharacterGlyphs) {
-            memset(mCharacterGlyphs, 0, sizeof(CompressedGlyph)*aLength);
-        }
-    }
+    
+    mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>
+        (reinterpret_cast<PRUint8*>(this) + aObjectSize);
+    memset(mCharacterGlyphs, 0, sizeof(CompressedGlyph)*aLength);
+    
     if (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
         mText.mSingle = static_cast<const PRUint8 *>(aText);
         if (!(mFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
-            PRUint8 *newText = new PRUint8[aLength];
-            if (!newText) {
-                // indicate textrun failure
-                mCharacterGlyphs = nsnull;
-            } else {
-                memcpy(newText, aText, aLength);
-            }
+            PRUint8 *newText = reinterpret_cast<PRUint8*>(mCharacterGlyphs + aLength);
+            memcpy(newText, aText, aLength);
             mText.mSingle = newText;    
         }
     } else {
         mText.mDouble = static_cast<const PRUnichar *>(aText);
         if (!(mFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
-            PRUnichar *newText = new PRUnichar[aLength];
-            if (!newText) {
-                // indicate textrun failure
-                mCharacterGlyphs = nsnull;
-            } else {
-                memcpy(newText, aText, aLength*sizeof(PRUnichar));
-            }
+            PRUnichar *newText = reinterpret_cast<PRUnichar*>(mCharacterGlyphs + aLength);
+            memcpy(newText, aText, aLength*sizeof(PRUnichar));
             mText.mDouble = newText;    
         }
     }
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     AccountStorageForTextRun(this, 1);
 #endif
 }
 
 gfxTextRun::~gfxTextRun()
 {
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     AccountStorageForTextRun(this, -1);
 #endif
-    if (!(mFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
-        if (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
-            delete[] mText.mSingle;
-        } else {
-            delete[] mText.mDouble;
-        }
-    }
     NS_RELEASE(mFontGroup);
     MOZ_COUNT_DTOR(gfxTextRun);
 }
 
 gfxTextRun *
 gfxTextRun::Clone(const gfxTextRunFactory::Parameters *aParams, const void *aText,
                   PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags)
 {
     if (!mCharacterGlyphs)
         return nsnull;
 
     nsAutoPtr<gfxTextRun> textRun;
-    textRun = new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
-    if (!textRun || !textRun->mCharacterGlyphs)
+    textRun = gfxTextRun::Create(aParams, aText, aLength, aFontGroup, aFlags);
+    if (!textRun)
         return nsnull;
 
     textRun->CopyGlyphDataFrom(this, 0, mCharacterCount, 0, PR_FALSE);
     return textRun.forget();
 }
 
 PRBool
 gfxTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
--- a/gfx/thebes/src/gfxTextRunWordCache.cpp
+++ b/gfx/thebes/src/gfxTextRunWordCache.cpp
@@ -364,17 +364,17 @@ TextRunWordCache::FinishTextRun(gfxTextR
 
 static gfxTextRun *
 MakeBlankTextRun(const void* aText, PRUint32 aLength,
                          gfxFontGroup *aFontGroup,
                          const gfxFontGroup::Parameters *aParams,
                          PRUint32 aFlags)
 {
     nsAutoPtr<gfxTextRun> textRun;
-    textRun = new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
+    textRun = gfxTextRun::Create(aParams, aText, aLength, aFontGroup, aFlags);
     if (!textRun || !textRun->GetCharacterGlyphs())
         return nsnull;
     gfxFont *font = aFontGroup->GetFontAt(0);
     textRun->AddGlyphRun(font, 0);
     return textRun.forget();
 }
 
 gfxTextRun *
@@ -386,17 +386,17 @@ TextRunWordCache::MakeTextRun(const PRUn
     if (aFontGroup->GetStyle()->size == 0) {
         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
         // them, and always create at least size 1 fonts, i.e. they still
         // render something for size 0 fonts.
         return MakeBlankTextRun(aText, aLength, aFontGroup, aParams, aFlags);
     }
 
     nsAutoPtr<gfxTextRun> textRun;
-    textRun = new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
+    textRun = gfxTextRun::Create(aParams, aText, aLength, aFontGroup, aFlags);
     if (!textRun || !textRun->GetCharacterGlyphs())
         return nsnull;
 #ifdef DEBUG
     textRun->mCachedWords = 0;
 #endif
 
     gfxFont *font = aFontGroup->GetFontAt(0);
     nsresult rv = textRun->AddGlyphRun(font, 0);
@@ -469,17 +469,17 @@ TextRunWordCache::MakeTextRun(const PRUi
         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
         // them, and always create at least size 1 fonts, i.e. they still
         // render something for size 0 fonts.
         return MakeBlankTextRun(aText, aLength, aFontGroup, aParams, aFlags);
     }
 
     aFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
     nsAutoPtr<gfxTextRun> textRun;
-    textRun = new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
+    textRun = gfxTextRun::Create(aParams, aText, aLength, aFontGroup, aFlags);
     if (!textRun || !textRun->GetCharacterGlyphs())
         return nsnull;
 #ifdef DEBUG
     textRun->mCachedWords = 0;
 #endif
 
     gfxFont *font = aFontGroup->GetFontAt(0);
     nsresult rv = textRun->AddGlyphRun(font, 0);
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -43,16 +43,29 @@
 #include "nsICaseConversion.h"
 #include "nsStyleConsts.h"
 #include "nsStyleContext.h"
 #include "gfxContext.h"
 #include "nsContentUtils.h"
 
 #define SZLIG 0x00DF
 
+nsTransformedTextRun *
+nsTransformedTextRun::Create(const gfxTextRunFactory::Parameters* aParams,
+                             nsTransformingTextRunFactory* aFactory,
+                             gfxFontGroup* aFontGroup,
+                             const PRUnichar* aString, PRUint32 aLength,
+                             const PRUint32 aFlags, nsStyleContext** aStyles,
+                             PRBool aOwnsFactory)
+{
+  return new (aLength, aFlags)
+    nsTransformedTextRun(aParams, aFactory, aFontGroup, aString, aLength,
+                         aFlags, aStyles, aOwnsFactory);
+}
+
 void
 nsTransformedTextRun::SetCapitalization(PRUint32 aStart, PRUint32 aLength,
                                         PRPackedBool* aCapitalization,
                                         gfxContext* aRefContext)
 {
   if (mCapitalize.IsEmpty()) {
     if (!mCapitalize.AppendElements(GetLength()))
       return;
@@ -127,18 +140,18 @@ nsTransformedTextRun::SetLineBreaks(PRUi
 
 gfxTextRun*
 nsTransformingTextRunFactory::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
                                           const gfxTextRunFactory::Parameters* aParams,
                                           gfxFontGroup* aFontGroup, PRUint32 aFlags,
                                           nsStyleContext** aStyles, PRBool aOwnsFactory)
 {
   nsTransformedTextRun* textRun =
-    new nsTransformedTextRun(aParams, this, aFontGroup,
-                             aString, aLength, aFlags, aStyles, aOwnsFactory);
+    nsTransformedTextRun::Create(aParams, this, aFontGroup,
+                                 aString, aLength, aFlags, aStyles, aOwnsFactory);
   if (!textRun)
     return nsnull;
 
   RebuildTextRun(textRun, aParams->mContext);
   return textRun;
 }
 
 gfxTextRun*
--- a/layout/generic/nsTextRunTransformations.h
+++ b/layout/generic/nsTextRunTransformations.h
@@ -95,34 +95,23 @@ protected:
 };
 
 /**
  * So that we can reshape as necessary, we store enough information
  * to fully rebuild the textrun contents.
  */
 class nsTransformedTextRun : public gfxTextRun {
 public:
-  nsTransformedTextRun(const gfxTextRunFactory::Parameters* aParams,
-                       nsTransformingTextRunFactory* aFactory,
-                       gfxFontGroup* aFontGroup,
-                       const PRUnichar* aString, PRUint32 aLength,
-                       const PRUint32 aFlags, nsStyleContext** aStyles,
-                       PRBool aOwnsFactory)
-    : gfxTextRun(aParams, aString, aLength, aFontGroup, aFlags),
-      mFactory(aFactory), mOwnsFactory(aOwnsFactory)
-  {
-    PRUint32 i;
-    for (i = 0; i < aLength; ++i) {
-      mStyles.AppendElement(aStyles[i]);
-    }
-    for (i = 0; i < aParams->mInitialBreakCount; ++i) {
-      mLineBreaks.AppendElement(aParams->mInitialBreaks[i]);
-    }
-  }
-  
+  static nsTransformedTextRun *Create(const gfxTextRunFactory::Parameters* aParams,
+                                      nsTransformingTextRunFactory* aFactory,
+                                      gfxFontGroup* aFontGroup,
+                                      const PRUnichar* aString, PRUint32 aLength,
+                                      const PRUint32 aFlags, nsStyleContext** aStyles,
+                                      PRBool aOwnsFactory);
+
   ~nsTransformedTextRun() {
     if (mOwnsFactory) {
       delete mFactory;
     }
   }
   
   virtual void SetCapitalization(PRUint32 aStart, PRUint32 aLength,
                                  PRPackedBool* aCapitalization,
@@ -135,11 +124,30 @@ public:
                                gfxFloat* aAdvanceWidthDelta,
                                gfxContext* aRefContext);
 
   nsTransformingTextRunFactory       *mFactory;
   nsTArray<PRUint32>                  mLineBreaks;
   nsTArray<nsRefPtr<nsStyleContext> > mStyles;
   nsTArray<PRPackedBool>              mCapitalize;
   PRPackedBool                        mOwnsFactory;
+
+private:
+  nsTransformedTextRun(const gfxTextRunFactory::Parameters* aParams,
+                       nsTransformingTextRunFactory* aFactory,
+                       gfxFontGroup* aFontGroup,
+                       const PRUnichar* aString, PRUint32 aLength,
+                       const PRUint32 aFlags, nsStyleContext** aStyles,
+                       PRBool aOwnsFactory)
+    : gfxTextRun(aParams, aString, aLength, aFontGroup, aFlags, sizeof(nsTransformedTextRun)),
+      mFactory(aFactory), mOwnsFactory(aOwnsFactory)
+  {
+    PRUint32 i;
+    for (i = 0; i < aLength; ++i) {
+      mStyles.AppendElement(aStyles[i]);
+    }
+    for (i = 0; i < aParams->mInitialBreakCount; ++i) {
+      mLineBreaks.AppendElement(aParams->mInitialBreaks[i]);
+    }
+  }  
 };
 
 #endif /*NSTEXTRUNTRANSFORMATIONS_H_*/