Commit 092c8b03 authored by Nicholas Nethercote's avatar Nicholas Nethercote
Browse files

Bug 712865 - Avoid some padding nsCSSCompressedDataBlock by storing nsCSSValue...

Bug 712865 - Avoid some padding nsCSSCompressedDataBlock by storing nsCSSValue and nsCSSProperty elements separately (2nd attempt).  r=dbaron.
parent 557c80bf
Loading
Loading
Loading
Loading
+46 −120
Original line number Diff line number Diff line
@@ -48,34 +48,6 @@

namespace css = mozilla::css;

enum {
    CDBValueStorage_advance = sizeof(CDBValueStorage)
};

/*
 * Define a bunch of utility functions for getting the property or any
 * of the value types when the cursor is at the beginning of the storage
 * for the property-value pair.  The versions taking a non-const cursor
 * argument return a reference so that the caller can assign into the
 * result.
 */

inline nsCSSProperty& PropertyAtCursor(char *aCursor) {
    return *reinterpret_cast<nsCSSProperty*>(aCursor);
}

inline nsCSSProperty PropertyAtCursor(const char *aCursor) {
    return *reinterpret_cast<const nsCSSProperty*>(aCursor);
}

inline nsCSSValue* ValueAtCursor(char *aCursor) {
    return & reinterpret_cast<CDBValueStorage*>(aCursor)->value;
}

inline const nsCSSValue* ValueAtCursor(const char *aCursor) {
    return & reinterpret_cast<const CDBValueStorage*>(aCursor)->value;
}

/**
 * Does a fast move of aSource to aDest.  The previous value in
 * aDest is cleanly destroyed, and aSource is cleared.  Returns
@@ -164,16 +136,13 @@ nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const

    nsIDocument* doc = aRuleData->mPresContext->Document();

    const char* cursor = Block();
    const char* cursor_end = BlockEnd();
    while (cursor < cursor_end) {
        nsCSSProperty iProp = PropertyAtCursor(cursor);
        NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
    for (PRUint32 i = 0; i < mNumProps; i++) {
        nsCSSProperty iProp = PropertyAtIndex(i);
        if (nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]) &
            aRuleData->mSIDs) {
            nsCSSValue* target = aRuleData->ValueFor(iProp);
            if (target->GetUnit() == eCSSUnit_Null) {
                const nsCSSValue *val = ValueAtCursor(cursor);
                const nsCSSValue *val = ValueAtIndex(i);
                NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null, "oops");
                if (ShouldStartImageLoads(aRuleData, iProp)) {
                    TryToStartImageLoad(*val, doc, iProp);
@@ -197,9 +166,7 @@ nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const
                }
            }
        }
        cursor += CDBValueStorage_advance;
    }
    NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");
}

const nsCSSValue*
@@ -216,17 +183,11 @@ nsCSSCompressedDataBlock::ValueFor(nsCSSProperty aProperty) const
          mStyleBits))
        return nsnull;

    const char* cursor = Block();
    const char* cursor_end = BlockEnd();
    while (cursor < cursor_end) {
        nsCSSProperty iProp = PropertyAtCursor(cursor);
        NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
        if (iProp == aProperty) {
            return ValueAtCursor(cursor);
    for (PRUint32 i = 0; i < mNumProps; i++) {
        if (PropertyAtIndex(i) == aProperty) {
            return ValueAtIndex(i);
        }
        cursor += CDBValueStorage_advance;
    }
    NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");

    return nsnull;
}
@@ -254,56 +215,35 @@ nsCSSCompressedDataBlock::TryReplaceValue(nsCSSProperty aProperty,
nsCSSCompressedDataBlock*
nsCSSCompressedDataBlock::Clone() const
{
    const char *cursor = Block(), *cursor_end = BlockEnd();
    char *result_cursor;

    nsAutoPtr<nsCSSCompressedDataBlock> result
        (new(cursor_end - cursor) nsCSSCompressedDataBlock());
    if (!result)
        return nsnull;
    result_cursor = result->Block();
    nsAutoPtr<nsCSSCompressedDataBlock>
        result(new(mNumProps) nsCSSCompressedDataBlock(mNumProps));

    while (cursor < cursor_end) {
        nsCSSProperty iProp = PropertyAtCursor(cursor);
        NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
        PropertyAtCursor(result_cursor) = iProp;
    result->mStyleBits = mStyleBits;

        const nsCSSValue* val = ValueAtCursor(cursor);
        nsCSSValue *result_val = ValueAtCursor(result_cursor);
        new (result_val) nsCSSValue(*val);
        cursor += CDBValueStorage_advance;
        result_cursor +=  CDBValueStorage_advance;
    for (PRUint32 i = 0; i < mNumProps; i++) {
        result->SetPropertyAtIndex(i, PropertyAtIndex(i));
        result->CopyValueToIndex(i, ValueAtIndex(i));
    }
    NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");

    result->SetBlockEnd(result_cursor);
    result->mStyleBits = mStyleBits;
    NS_ABORT_IF_FALSE(result->DataSize() == DataSize(), "wrong size");

    return result.forget();
}

nsCSSCompressedDataBlock::~nsCSSCompressedDataBlock()
{
    const char* cursor = Block();
    const char* cursor_end = BlockEnd();
    while (cursor < cursor_end) {
        NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(PropertyAtCursor(cursor)),
                          "out of range");

        const nsCSSValue* val = ValueAtCursor(cursor);
    for (PRUint32 i = 0; i < mNumProps; i++) {
#ifdef DEBUG
        (void)PropertyAtIndex(i);   // this checks the property is in range
#endif
        const nsCSSValue* val = ValueAtIndex(i);
        NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null, "oops");
        val->~nsCSSValue();
        cursor += CDBValueStorage_advance;
    }
    NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");
}

/* static */ nsCSSCompressedDataBlock*
nsCSSCompressedDataBlock::CreateEmptyBlock()
{
    nsCSSCompressedDataBlock *result = new(0) nsCSSCompressedDataBlock();
    result->SetBlockEnd(result->Block());
    nsCSSCompressedDataBlock *result = new(0) nsCSSCompressedDataBlock(0);
    return result;
}

@@ -311,12 +251,8 @@ size_t
nsCSSCompressedDataBlock::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
{
    size_t n = aMallocSizeOf(this);

    const char* cursor = Block();
    const char* cursor_end = BlockEnd();
    while (cursor < cursor_end) {
        n += ValueAtCursor(cursor)->SizeOfExcludingThis(aMallocSizeOf);
        cursor += CDBValueStorage_advance;
    for (PRUint32 i = 0; i < mNumProps; i++) {
        n += ValueAtIndex(i)->SizeOfExcludingThis(aMallocSizeOf);
    }
    return n;
}
@@ -341,10 +277,8 @@ nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock,
     * Save needless copying and allocation by copying the memory
     * corresponding to the stored data in the compressed block.
     */
    const char* cursor = aBlock->Block();
    const char* cursor_end = aBlock->BlockEnd();
    while (cursor < cursor_end) {
        nsCSSProperty iProp = PropertyAtCursor(cursor);
    for (PRUint32 i = 0; i < aBlock->mNumProps; i++) {
        nsCSSProperty iProp = aBlock->PropertyAtIndex(i);
        NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
        NS_ABORT_IF_FALSE(!HasPropertyBit(iProp),
                          "compressed block has property multiple times");
@@ -352,7 +286,7 @@ nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock,
        if (aImportant)
            SetImportantBit(iProp);

        const nsCSSValue* val = ValueAtCursor(cursor);
        const nsCSSValue* val = aBlock->ValueAtIndex(i);
        nsCSSValue* dest = PropertyAt(iProp);
        NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null, "oops");
        NS_ABORT_IF_FALSE(dest->GetUnit() == eCSSUnit_Null,
@@ -361,12 +295,11 @@ nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock,
        dest->~nsCSSValue();
#endif
        memcpy(dest, val, sizeof(nsCSSValue));
        cursor += CDBValueStorage_advance;
    }
    NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");

    // Don't destroy remnants of what we just copied
    aBlock->SetBlockEnd(aBlock->Block());
    // Set the number of properties to zero so that we don't destroy the
    // remnants of what we just copied.
    aBlock->SetNumPropsToZero();
    delete aBlock;
}

@@ -383,10 +316,11 @@ nsCSSExpandedDataBlock::Expand(nsCSSCompressedDataBlock *aNormalBlock,
    }
}

nsCSSExpandedDataBlock::ComputeSizeResult
nsCSSExpandedDataBlock::ComputeSize()
void
nsCSSExpandedDataBlock::ComputeNumProps(PRUint32* aNumPropsNormal,
                                        PRUint32* aNumPropsImportant)
{
    ComputeSizeResult result = {0, 0};
    *aNumPropsNormal = *aNumPropsImportant = 0;
    for (size_t iHigh = 0; iHigh < nsCSSPropertySet::kChunkCount; ++iHigh) {
        if (!mPropertiesSet.HasPropertyInChunk(iHigh))
            continue;
@@ -400,12 +334,11 @@ nsCSSExpandedDataBlock::ComputeSize()
            NS_ABORT_IF_FALSE(PropertyAt(iProp)->GetUnit() != eCSSUnit_Null,
                              "null value while computing size");
            if (mPropertiesImportant.HasPropertyAt(iHigh, iLow))
                result.important += CDBValueStorage_advance;
                (*aNumPropsImportant)++;
            else
                result.normal += CDBValueStorage_advance;
                (*aNumPropsNormal)++;
        }
    }
    return result;
}

void
@@ -413,19 +346,19 @@ nsCSSExpandedDataBlock::Compress(nsCSSCompressedDataBlock **aNormalBlock,
                                 nsCSSCompressedDataBlock **aImportantBlock)
{
    nsAutoPtr<nsCSSCompressedDataBlock> result_normal, result_important;
    char *cursor_normal, *cursor_important;
    PRUint32 i_normal = 0, i_important = 0;

    ComputeSizeResult size = ComputeSize();
    PRUint32 numPropsNormal, numPropsImportant;
    ComputeNumProps(&numPropsNormal, &numPropsImportant);

    result_normal = new(size.normal) nsCSSCompressedDataBlock();
    cursor_normal = result_normal->Block();
    result_normal =
        new(numPropsNormal) nsCSSCompressedDataBlock(numPropsNormal);

    if (size.important != 0) {
        result_important = new(size.important) nsCSSCompressedDataBlock();
        cursor_important = result_important->Block();
    if (numPropsImportant != 0) {
        result_important =
            new(numPropsImportant) nsCSSCompressedDataBlock(numPropsImportant);
    } else {
        result_important = nsnull;
        cursor_important = nsnull;
    }

    /*
@@ -443,32 +376,25 @@ nsCSSExpandedDataBlock::Compress(nsCSSCompressedDataBlock **aNormalBlock,
            NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
            bool important =
                mPropertiesImportant.HasPropertyAt(iHigh, iLow);
            char *&cursor = important ? cursor_important : cursor_normal;
            nsCSSCompressedDataBlock *result =
                important ? result_important : result_normal;
            PRUint32* ip = important ? &i_important : &i_normal;
            nsCSSValue* val = PropertyAt(iProp);
            NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null,
                              "Null value while compressing");
            CDBValueStorage *storage =
                reinterpret_cast<CDBValueStorage*>(cursor);
            storage->property = iProp;
            memcpy(&storage->value, val, sizeof(nsCSSValue));
            result->SetPropertyAtIndex(*ip, iProp);
            result->RawCopyValueToIndex(*ip, val);
            new (val) nsCSSValue();
            cursor += CDBValueStorage_advance;
            (*ip)++;
            result->mStyleBits |=
                nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]);
        }
    }

    result_normal->SetBlockEnd(cursor_normal);
    NS_ABORT_IF_FALSE(result_normal->DataSize() == ptrdiff_t(size.normal),
                      "size miscalculation");
    NS_ABORT_IF_FALSE(numPropsNormal == i_normal, "bad numProps");

    if (result_important) {
        result_important->SetBlockEnd(cursor_important);
        NS_ABORT_IF_FALSE(result_important->DataSize() ==
                          ptrdiff_t(size.important),
                          "size miscalculation");
        NS_ABORT_IF_FALSE(numPropsImportant == i_important, "bad numProps");
    }

    ClearSets();
+75 −45
Original line number Diff line number Diff line
@@ -55,16 +55,6 @@ class Declaration;
}
}

/*
 * nsCSSCompressedDataBlock holds property-value pairs corresponding
 * to CSS declaration blocks.  Each pair is stored in a CDBValueStorage
 * object; these objects form an array at the end of the data block.
 */
struct CDBValueStorage {
    nsCSSProperty property;
    nsCSSValue value;
};

/**
 * An |nsCSSCompressedDataBlock| holds a usually-immutable chunk of
 * property-value data for a CSS declaration block (which we misname a
@@ -77,7 +67,9 @@ private:

    // Only this class (via |CreateEmptyBlock|) or nsCSSExpandedDataBlock
    // (in |Compress|) can create compressed data blocks.
    nsCSSCompressedDataBlock() : mStyleBits(0) {}
    nsCSSCompressedDataBlock(PRUint32 aNumProps)
      : mStyleBits(0), mNumProps(aNumProps)
    {}

public:
    ~nsCSSCompressedDataBlock();
@@ -124,47 +116,87 @@ public:
    size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;

private:
    void* operator new(size_t aBaseSize, size_t aDataSize) {
    void* operator new(size_t aBaseSize, PRUint32 aNumProps) {
        NS_ABORT_IF_FALSE(aBaseSize == sizeof(nsCSSCompressedDataBlock),
                          "unexpected size for nsCSSCompressedDataBlock");
        return ::operator new(aBaseSize + aDataSize);
        return ::operator new(aBaseSize + DataSize(aNumProps));
    }

    /**
     * Delete all the data stored in this block, and the block itself.
     */
    void Destroy();
public:
    // Ideally, |nsCSSProperty| would be |enum nsCSSProperty : PRInt16|.  But
    // not all of the compilers we use are modern enough to support small
    // enums.  So we manually squeeze nsCSSProperty into 16 bits ourselves.
    // The static assertion below ensures it fits.
    typedef PRInt16 CompressedCSSProperty;
    static const size_t MaxCompressedCSSProperty = PR_INT16_MAX;

private:
    static size_t DataSize(PRUint32 aNumProps) {
        return size_t(aNumProps) *
               (sizeof(nsCSSValue) + sizeof(CompressedCSSProperty));
    }

    PRInt32 mStyleBits; // the structs for which we have data, according to
                        // |nsCachedStyleData::GetBitForSID|.
    PRUint32 mDataSize;
    // CDBValueStorage elements are stored after these fields.  Space for them
    PRUint32 mNumProps;
    // nsCSSValue elements are stored after these fields, and
    // nsCSSProperty elements are stored -- each one compressed as a
    // CompressedCSSProperty -- after the nsCSSValue elements.  Space for them
    // is allocated in |operator new| above.  The static assertions following
    // this class make sure that the CDBValueStorage elements are aligned
    // this class make sure that the value and property elements are aligned
    // appropriately.

    char* Block() { return (char*)this + sizeof(*this); }
    char* BlockEnd() { return Block() + mDataSize; }
    const char* Block() const { return (char*)this + sizeof(*this); }
    const char* BlockEnd() const { return Block() + mDataSize; }
    void SetBlockEnd(char *blockEnd) { 
        /*
         * Note:  if we ever change nsCSSDeclaration to store the declarations
         * in order and also store repeated declarations of the same property,
         * then we need to worry about checking for integer overflow here.
         */
        NS_ABORT_IF_FALSE(size_t(blockEnd - Block()) <= size_t(PR_UINT32_MAX),
                          "overflow of mDataSize");
        mDataSize = PRUint32(blockEnd - Block());
    nsCSSValue* Values() const {
        return (nsCSSValue*)(this + 1);
    }

    CompressedCSSProperty* CompressedProperties() const {
        return (CompressedCSSProperty*)(Values() + mNumProps);
    }

    nsCSSValue* ValueAtIndex(PRUint32 i) const {
        NS_ABORT_IF_FALSE(i < mNumProps, "value index out of range");
        return Values() + i;
    }

    nsCSSProperty PropertyAtIndex(PRUint32 i) const {
        NS_ABORT_IF_FALSE(i < mNumProps, "property index out of range");
        nsCSSProperty prop = (nsCSSProperty)CompressedProperties()[i];
        NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(prop), "out of range");
        return prop;
    }

    void CopyValueToIndex(PRUint32 i, nsCSSValue* aValue) {
        new (ValueAtIndex(i)) nsCSSValue(*aValue);
    }

    void RawCopyValueToIndex(PRUint32 i, nsCSSValue* aValue) {
        memcpy(ValueAtIndex(i), aValue, sizeof(nsCSSValue));
    }

    void SetPropertyAtIndex(PRUint32 i, nsCSSProperty aProperty) {
        NS_ABORT_IF_FALSE(i < mNumProps, "set property index out of range");
        CompressedProperties()[i] = (CompressedCSSProperty)aProperty;
    }

    void SetNumPropsToZero() {
        mNumProps = 0;
    }
    ptrdiff_t DataSize() const { return mDataSize; }
};

/* Make sure the CDBValueStorage elements are aligned appropriately. */
// Make sure the values and properties are aligned appropriately.  (These
// assertions are stronger than necessary to keep them simple.)
MOZ_STATIC_ASSERT(sizeof(nsCSSCompressedDataBlock) == 8,
                  "nsCSSCompressedDataBlock's size has changed");
MOZ_STATIC_ASSERT(NS_ALIGNMENT_OF(CDBValueStorage) <= 8,
                  "CDBValueStorage needs too much alignment");
MOZ_STATIC_ASSERT(NS_ALIGNMENT_OF(nsCSSValue) == 4 || NS_ALIGNMENT_OF(nsCSSValue) == 8,
                  "nsCSSValue doesn't align with nsCSSCompressedDataBlock"); 
MOZ_STATIC_ASSERT(NS_ALIGNMENT_OF(nsCSSCompressedDataBlock::CompressedCSSProperty) == 2,
                  "CompressedCSSProperty doesn't align with nsCSSValue"); 

// Make sure that sizeof(CompressedCSSProperty) is big enough.
MOZ_STATIC_ASSERT(eCSSProperty_COUNT_no_shorthands <=
                  nsCSSCompressedDataBlock::MaxCompressedCSSProperty,
                  "nsCSSProperty doesn't fit in StoredSizeOfCSSProperty");

class nsCSSExpandedDataBlock {
    friend class nsCSSCompressedDataBlock;
@@ -251,13 +283,11 @@ public:

private:
    /**
     * Compute the size that will be occupied by the result of
     * |Compress|.
     * Compute the number of properties that will be present in the
     * result of |Compress|.
     */
    struct ComputeSizeResult {
        PRUint32 normal, important;
    };
    ComputeSizeResult ComputeSize();
    void ComputeNumProps(PRUint32* aNumPropsNormal,
                         PRUint32* aNumPropsImportant);
    
    void DoExpand(nsCSSCompressedDataBlock *aBlock, bool aImportant);