Bug 187610: Use PLDHash for keys

r=Pike sr=peterv
This commit is contained in:
sicking%bigfoot.com 2005-11-02 07:39:59 +00:00
Родитель 1658c0576c
Коммит 18ec9f51c6
4 изменённых файлов: 460 добавлений и 203 удалений

Просмотреть файл

@ -0,0 +1,216 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is TransforMiiX XSLT processor.
*
* The Initial Developer of the Original Code is
* Jonas Sicking.
* Portions created by the Initial Developer are Copyright (C) 2003
* Jonas Sicking. All Rights Reserved.
*
* Contributor(s):
* Jonas Sicking <jonas@sicking.cc>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef txKey_h__
#define txKey_h__
#include "nsDoubleHashTable.h"
#include "XMLUtils.h"
class txKeyValueHashKey
{
public:
txKeyValueHashKey(const txExpandedName& aKeyName,
Document* aDocument,
const nsAString& aKeyValue)
: mKeyName(aKeyName),
mKeyValue(aKeyValue),
mDocument(aDocument)
{
}
txExpandedName mKeyName;
nsString mKeyValue;
Document* mDocument;
};
struct txKeyValueHashEntry : public PLDHashEntryHdr
{
txKeyValueHashEntry(const void* aKey)
: mKey(*NS_STATIC_CAST(const txKeyValueHashKey*, aKey))
{
}
// @see nsDoubleHashtable.h
const void* GetKey();
PRBool MatchEntry(const void* aKey) const;
static PLDHashNumber HashKey(const void* aKey);
txKeyValueHashKey mKey;
NodeSet mNodeSet;
};
DECL_DHASH_WRAPPER(txKeyValueHash, txKeyValueHashEntry, txKeyValueHashKey&);
class txIndexedKeyHashKey
{
public:
txIndexedKeyHashKey(txExpandedName aKeyName,
Document* aDocument)
: mKeyName(aKeyName),
mDocument(aDocument)
{
}
txExpandedName mKeyName;
Document* mDocument;
};
struct txIndexedKeyHashEntry : public PLDHashEntryHdr
{
txIndexedKeyHashEntry(const void* aKey)
: mKey(*NS_STATIC_CAST(const txIndexedKeyHashKey*, aKey)),
mIndexed(PR_FALSE)
{
}
// @see nsDoubleHashtable.h
const void* GetKey();
PRBool MatchEntry(const void* aKey) const;
static PLDHashNumber HashKey(const void* aKey);
txIndexedKeyHashKey mKey;
PRBool mIndexed;
};
DECL_DHASH_WRAPPER(txIndexedKeyHash, txIndexedKeyHashEntry,
txIndexedKeyHashKey&);
/**
* Class holding all <xsl:key>s of a particular expanded name in the
* stylesheet.
*/
class txXSLKey : public TxObject {
public:
txXSLKey(const txExpandedName& aName) : mName(aName)
{
}
~txXSLKey();
/**
* Adds a match/use pair.
* @param aMatch match-pattern
* @param aUse use-expression
* @return PR_FALSE if an error occured, PR_TRUE otherwise
*/
PRBool addKey(txPattern* aMatch, Expr* aUse);
/**
* Indexes a document and adds it to the hash of key values
* @param aDocument Document to index and add
* @param aKeyValueHash Hash to add values to
* @param aPs ProcessorState to use for XPath evaluation
*/
nsresult indexDocument(Document* aDocument,
txKeyValueHash& aKeyValueHash,
ProcessorState* aPs);
private:
/**
* Recursively searches a node, its attributes and its subtree for
* nodes matching any of the keys match-patterns.
* @param aNode Node to search
* @param aKey Key to use when adding into the hash
* @param aKeyValueHash Hash to add values to
* @param aPs ProcessorState to use for XPath evaluation
*/
nsresult indexTree(Node* aNode, txKeyValueHashKey& aKey,
txKeyValueHash& aKeyValueHash, ProcessorState* aPs);
/**
* Tests one node if it matches any of the keys match-patterns. If
* the node matches its values are added to the index.
* @param aNode Node to test
* @param aKey Key to use when adding into the hash
* @param aKeyValueHash Hash to add values to
* @param aPs ProcessorState to use for XPath evaluation
*/
nsresult testNode(Node* aNode, txKeyValueHashKey& aKey,
txKeyValueHash& aKeyValueHash, ProcessorState* aPs);
/**
* represents one match/use pair
*/
struct Key {
txPattern* matchPattern;
Expr* useExpr;
};
/**
* List of all match/use pairs. The items as |Key|s
*/
List mKeys;
/**
* Name of this key
*/
txExpandedName mName;
};
class txKeyHash
{
public:
txKeyHash(txExpandedNameMap& aKeys)
: mKeys(aKeys)
{
}
nsresult init();
nsresult getKeyNodes(const txExpandedName& aKeyName,
Document* aDocument,
const nsAString& aKeyValue,
PRBool aIndexIfNotFound,
ProcessorState* aPs,
const NodeSet** aResult);
private:
// Hash of all indexed key-values
txKeyValueHash mKeyValues;
// Hash showing which keys+documents has been indexed
txIndexedKeyHash mIndexedKeys;
// Map of txXSLKeys
txExpandedNameMap& mKeys;
};
#endif //txKey_h__

Просмотреть файл

@ -24,6 +24,8 @@
#include "txSingleNodeContext.h"
#include "XMLDOMUtils.h"
#include "XSLTFunctions.h"
#include "nsReadableUtils.h"
#include "txKey.h"
/*
* txKeyFunctionCall
@ -62,23 +64,16 @@ ExprResult* txKeyFunctionCall::evaluate(txIEvalContext* aContext)
txListIterator iter(&params);
nsAutoString keyQName;
evaluateToString((Expr*)iter.next(), aContext, keyQName);
Expr* param = (Expr*) iter.next();
txExpandedName keyName;
txXSLKey* key = 0;
nsresult rv = keyName.init(keyQName, mQNameResolveNode, MB_FALSE);
if (NS_SUCCEEDED(rv)) {
key = mProcessorState->getKey(keyName);
nsresult rv = keyName.init(keyQName, mQNameResolveNode, PR_FALSE);
if (NS_FAILED(rv)) {
delete res;
return new StringResult(NS_LITERAL_STRING("error"));
}
if (!key) {
nsAutoString err(NS_LITERAL_STRING("No key with that name in: "));
toString(err);
aContext->receiveError(err, NS_ERROR_INVALID_ARG);
return res;
}
ExprResult* exprResult = param->evaluate(aContext);
ExprResult* exprResult = ((Expr*)iter.next())->evaluate(aContext);
if (!exprResult)
return res;
@ -91,21 +86,41 @@ ExprResult* txKeyFunctionCall::evaluate(txIEvalContext* aContext)
if (exprResult->getResultType() == ExprResult::NODESET) {
NodeSet* nodeSet = (NodeSet*) exprResult;
for (int i=0; i<nodeSet->size(); i++) {
int i;
for (i = 0; i < nodeSet->size(); ++i) {
nsAutoString val;
XMLDOMUtils::getNodeValue(nodeSet->get(i), val);
res->add(key->getNodes(val, contextDoc));
const NodeSet* nodes = 0;
rv = mProcessorState->getKeyNodes(keyName, contextDoc, val,
i == 0, &nodes);
if (NS_FAILED(rv)) {
delete res;
delete exprResult;
return new StringResult(NS_LITERAL_STRING("error"));
}
if (nodes) {
res->add(nodes);
}
}
}
else {
nsAutoString val;
exprResult->stringValue(val);
res->append(key->getNodes(val, contextDoc));
const NodeSet* nodes = 0;
rv = mProcessorState->getKeyNodes(keyName, contextDoc, val,
PR_TRUE, &nodes);
if (NS_FAILED(rv)) {
delete res;
delete exprResult;
return new StringResult(NS_LITERAL_STRING("error"));
}
if (nodes) {
res->append(nodes);
}
}
delete exprResult;
return res;
} // evaluate
}
nsresult txKeyFunctionCall::getNameAtom(nsIAtom** aAtom)
{
@ -114,17 +129,148 @@ nsresult txKeyFunctionCall::getNameAtom(nsIAtom** aAtom)
return NS_OK;
}
/*
* Class representing an <xsl:key>. Or in the case where several <xsl:key>s
* have the same name one object represents all <xsl:key>s with that name
/**
* Hash functions
*/
txXSLKey::txXSLKey(ProcessorState* aPs)
{
mProcessorState = aPs;
mMaps.setOwnership(Map::eOwnsItems);
} // txXSLKey
DHASH_WRAPPER(txKeyValueHash, txKeyValueHashEntry, txKeyValueHashKey&);
DHASH_WRAPPER(txIndexedKeyHash, txIndexedKeyHashEntry, txIndexedKeyHashKey&);
const void*
txKeyValueHashEntry::GetKey()
{
return &mKey;
}
PRBool
txKeyValueHashEntry::MatchEntry(const void* aKey) const
{
const txKeyValueHashKey* key =
NS_STATIC_CAST(const txKeyValueHashKey*, aKey);
return mKey.mKeyName == key->mKeyName &&
mKey.mDocument == key->mDocument &&
mKey.mKeyValue.Equals(key->mKeyValue);
}
PLDHashNumber
txKeyValueHashEntry::HashKey(const void* aKey)
{
const txKeyValueHashKey* key =
NS_STATIC_CAST(const txKeyValueHashKey*, aKey);
return key->mKeyName.mNamespaceID ^
NS_PTR_TO_INT32(key->mKeyName.mLocalName.get()) ^
NS_PTR_TO_INT32(key->mDocument) ^
HashString(key->mKeyValue);
}
const void*
txIndexedKeyHashEntry::GetKey()
{
return &mKey;
}
PRBool
txIndexedKeyHashEntry::MatchEntry(const void* aKey) const
{
const txIndexedKeyHashKey* key =
NS_STATIC_CAST(const txIndexedKeyHashKey*, aKey);
return mKey.mKeyName == key->mKeyName &&
mKey.mDocument == key->mDocument;
}
PLDHashNumber
txIndexedKeyHashEntry::HashKey(const void* aKey)
{
const txIndexedKeyHashKey* key =
NS_STATIC_CAST(const txIndexedKeyHashKey*, aKey);
return key->mKeyName.mNamespaceID ^
NS_PTR_TO_INT32(key->mKeyName.mLocalName.get()) ^
NS_PTR_TO_INT32(key->mDocument);
}
/*
* Class managing XSLT-keys
*/
nsresult
txKeyHash::getKeyNodes(const txExpandedName& aKeyName,
Document* aDocument,
const nsAString& aKeyValue,
PRBool aIndexIfNotFound,
ProcessorState* aPs,
const NodeSet** aResult)
{
NS_ENSURE_TRUE(mKeyValues.mHashTable.ops && mIndexedKeys.mHashTable.ops,
NS_ERROR_OUT_OF_MEMORY);
*aResult = nsnull;
txKeyValueHashKey valueKey(aKeyName, aDocument, aKeyValue);
txKeyValueHashEntry* valueEntry = mKeyValues.GetEntry(valueKey);
if (valueEntry) {
*aResult = &valueEntry->mNodeSet;
return NS_OK;
}
// We didn't find a value. This could either mean that that key has no
// nodes with that value or that the key hasn't been indexed using this
// document.
if (!aIndexIfNotFound) {
// If aIndexIfNotFound is set then the caller knows this key is
// indexed, so don't bother investigating.
return NS_OK;
}
txIndexedKeyHashKey indexKey(aKeyName, aDocument);
txIndexedKeyHashEntry* indexEntry = mIndexedKeys.AddEntry(indexKey);
NS_ENSURE_TRUE(indexEntry, NS_ERROR_OUT_OF_MEMORY);
if (indexEntry->mIndexed) {
// The key was indexed and apparently didn't contain this value so
// return null.
return NS_OK;
}
// The key needs to be indexed.
txXSLKey* xslKey = (txXSLKey*)mKeys.get(aKeyName);
if (!xslKey) {
// The key didn't exist, so bail.
return NS_ERROR_INVALID_ARG;
}
nsresult rv = xslKey->indexDocument(aDocument, mKeyValues, aPs);
NS_ENSURE_SUCCESS(rv, rv);
indexEntry->mIndexed = PR_TRUE;
// Now that the key is indexed we can get its value.
valueEntry = mKeyValues.GetEntry(valueKey);
if (valueEntry) {
*aResult = &valueEntry->mNodeSet;
}
return NS_OK;
}
nsresult
txKeyHash::init()
{
nsresult rv = mKeyValues.Init(8);
NS_ENSURE_SUCCESS(rv, rv);
rv = mIndexedKeys.Init(1);
NS_ENSURE_SUCCESS(rv, rv);
}
/**
* Class holding all <xsl:key>s of a particular expanded name in the
* stylesheet.
*/
txXSLKey::~txXSLKey()
{
txListIterator iter(&mKeys);
@ -134,150 +280,133 @@ txXSLKey::~txXSLKey()
delete key->useExpr;
delete key;
}
} // ~txXSLKey
}
/*
* Returns a NodeSet containing all nodes within the specified document
* that have the value keyValue. The document is indexed in case it
* hasn't been searched previously. The returned nodeset is owned by
* the txXSLKey object
* @param aKeyValue Value to search for
* @param aDoc Document to search in
* @return a NodeSet* containing all nodes in doc matching with value
* keyValue
*/
const NodeSet* txXSLKey::getNodes(const nsAString& aKeyValue, Document* aDoc)
{
NS_ASSERTION(aDoc, "missing document");
if (!aDoc)
return &mEmptyNodeset;
NamedMap* map = (NamedMap*)mMaps.get(aDoc);
if (!map) {
map = addDocument(aDoc);
if (!map)
return &mEmptyNodeset;
}
NodeSet* nodes = (NodeSet*)map->get(aKeyValue);
if (!nodes)
return &mEmptyNodeset;
return nodes;
} // getNodes
/*
* Adds a match/use pair. Returns MB_FALSE if matchString or useString
* can't be parsed.
/**
* Adds a match/use pair.
* @param aMatch match-pattern
* @param aUse use-expression
* @return MB_FALSE if an error occured, MB_TRUE otherwise
* @return PR_FALSE if an error occured, PR_TRUE otherwise
*/
MBool txXSLKey::addKey(txPattern* aMatch, Expr* aUse)
PRBool txXSLKey::addKey(txPattern* aMatch, Expr* aUse)
{
if (!aMatch || !aUse)
return MB_FALSE;
return PR_FALSE;
Key* key = new Key;
if (!key)
return MB_FALSE;
return PR_FALSE;
key->matchPattern = aMatch;
key->useExpr = aUse;
mKeys.add(key);
return MB_TRUE;
} // addKey
return PR_TRUE;
}
/*
* Indexes a document and adds it to the set of indexed documents
* @param aDoc Document to index and add
* @returns a NamedMap* containing the index
/**
* Indexes a document and adds it to the hash of key values
* @param aDocument Document to index and add
* @param aKeyValueHash Hash to add values to
* @param aPs ProcessorState to use for XPath evaluation
*/
NamedMap* txXSLKey::addDocument(Document* aDoc)
nsresult txXSLKey::indexDocument(Document* aDocument,
txKeyValueHash& aKeyValueHash,
ProcessorState* aPs)
{
NamedMap* map = new NamedMap;
if (!map)
return nsnull;
map->setObjectDeletion(MB_TRUE);
mMaps.put(aDoc, map);
indexTree(aDoc, map);
return map;
} // addDocument
txKeyValueHashKey key(mName, aDocument, NS_LITERAL_STRING(""));
return indexTree(aDocument, key, aKeyValueHash, aPs);
}
/*
/**
* Recursively searches a node, its attributes and its subtree for
* nodes matching any of the keys match-patterns.
* @param aNode node to search
* @param aMap index to add search result in
* @param aNode Node to search
* @param aKey Key to use when adding into the hash
* @param aKeyValueHash Hash to add values to
* @param aPs ProcessorState to use for XPath evaluation
*/
void txXSLKey::indexTree(Node* aNode, NamedMap* aMap)
nsresult txXSLKey::indexTree(Node* aNode, txKeyValueHashKey& aKey,
txKeyValueHash& aKeyValueHash,
ProcessorState* aPs)
{
testNode(aNode, aMap);
nsresult rv = testNode(aNode, aKey, aKeyValueHash, aPs);
NS_ENSURE_SUCCESS(rv, rv);
// check if the nodes attributes matches
NamedNodeMap* attrs = aNode->getAttributes();
if (attrs) {
for (PRUint32 i=0; i<attrs->getLength(); i++) {
testNode(attrs->item(i), aMap);
rv = testNode(attrs->item(i), aKey, aKeyValueHash, aPs);
NS_ENSURE_SUCCESS(rv, rv);
}
}
Node* child = aNode->getFirstChild();
while (child) {
indexTree(child, aMap);
rv = indexTree(child, aKey, aKeyValueHash, aPs);
NS_ENSURE_SUCCESS(rv, rv);
child = child->getNextSibling();
}
} // indexTree
return NS_OK;
}
/*
/**
* Tests one node if it matches any of the keys match-patterns. If
* the node matches its values are added to the index.
* @param aNode node to test
* @param aMap index to add values to
* @param aNode Node to test
* @param aKey Key to use when adding into the hash
* @param aKeyValueHash Hash to add values to
* @param aPs ProcessorState to use for XPath evaluation
*/
void txXSLKey::testNode(Node* aNode, NamedMap* aMap)
nsresult txXSLKey::testNode(Node* aNode, txKeyValueHashKey& aKey,
txKeyValueHash& aKeyValueHash, ProcessorState* aPs)
{
nsAutoString val;
NodeSet *nodeSet;
txListIterator iter(&mKeys);
while (iter.hasNext())
{
Key* key=(Key*)iter.next();
if (key->matchPattern->matches(aNode, mProcessorState)) {
txSingleNodeContext evalContext(aNode, mProcessorState);
if (key->matchPattern->matches(aNode, aPs)) {
txSingleNodeContext evalContext(aNode, aPs);
txIEvalContext* prevCon =
mProcessorState->setEvalContext(&evalContext);
aPs->setEvalContext(&evalContext);
ExprResult* exprResult = key->useExpr->evaluate(&evalContext);
mProcessorState->setEvalContext(prevCon);
aPs->setEvalContext(prevCon);
if (exprResult->getResultType() == ExprResult::NODESET) {
NodeSet* res = (NodeSet*)exprResult;
for (int i=0; i<res->size(); i++) {
val.Truncate();
XMLDOMUtils::getNodeValue(res->get(i), val);
nodeSet = (NodeSet*)aMap->get(val);
if (!nodeSet) {
nodeSet = new NodeSet;
if (!nodeSet)
return;
aMap->put(val, nodeSet);
aKey.mKeyValue.Assign(val);
txKeyValueHashEntry* entry = aKeyValueHash.AddEntry(aKey);
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
if (entry->mNodeSet.isEmpty() ||
entry->mNodeSet.get(entry->mNodeSet.size() - 1) !=
aNode) {
entry->mNodeSet.append(aNode);
}
nodeSet->append(aNode);
}
}
else {
exprResult->stringValue(val);
nodeSet = (NodeSet*)aMap->get(val);
if (!nodeSet) {
nodeSet = new NodeSet;
if (!nodeSet)
return;
aMap->put(val, nodeSet);
aKey.mKeyValue.Assign(val);
txKeyValueHashEntry* entry = aKeyValueHash.AddEntry(aKey);
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
if (entry->mNodeSet.isEmpty() ||
entry->mNodeSet.get(entry->mNodeSet.size()-1) !=
aNode) {
entry->mNodeSet.append(aNode);
}
nodeSet->append(aNode);
}
delete exprResult;
}
}
} // testNode
return NS_OK;
}

Просмотреть файл

@ -35,9 +35,7 @@
#include "Map.h"
#include "NodeSet.h"
class NamedMap;
class ProcessorState;
class txPattern;
/**
* The definition for the XSLT document() function
@ -77,93 +75,6 @@ private:
Node* mQNameResolveNode;
};
/*
* Class representing an <xsl:key>. Or in the case where several <xsl:key>s
* have the same name one object represents all <xsl:key>s with that name
*/
class txXSLKey : public TxObject {
public:
txXSLKey(ProcessorState* aPs);
~txXSLKey();
/*
* Returns a NodeSet containing all nodes within the specified document
* that have the value keyValue. The document is indexed in case it
* hasn't been searched previously. The returned nodeset is owned by
* the txXSLKey object
* @param aKeyValue Value to search for
* @param aDoc Document to search in
* @return a NodeSet* containing all nodes in doc matching with value
* keyValue
*/
const NodeSet* getNodes(const nsAString& aKeyValue, Document* aDoc);
/*
* Adds a match/use pair. Returns MB_FALSE if matchString or useString
* can't be parsed.
* @param aMatch match-pattern
* @param aUse use-expression
* @return MB_FALSE if an error occured, MB_TRUE otherwise
*/
MBool addKey(txPattern* aMatch, Expr* aUse);
private:
/*
* Indexes a document and adds it to the set of indexed documents
* @param aDoc Document to index and add
* @returns a NamedMap* containing the index
*/
NamedMap* addDocument(Document* aDoc);
/*
* Recursively searches a node, its attributes and its subtree for
* nodes matching any of the keys match-patterns.
* @param aNode node to search
* @param aMap index to add search result in
*/
void indexTree(Node* aNode, NamedMap* aMap);
/*
* Tests one node if it matches any of the keys match-patterns. If
* the node matches its values are added to the index.
* @param aNode node to test
* @param aMap index to add values to
*/
void testNode(Node* aNode, NamedMap* aMap);
/*
* represents one match/use pair
*/
struct Key {
txPattern* matchPattern;
Expr* useExpr;
};
/*
* List of all match/use pairs
*/
List mKeys;
/*
* Map containing all indexes (keyed on document). Every index is a
* NamedMap. Every NamedMap contains NodeLists with the nodes for
* a certain value
*/
Map mMaps;
/*
* ProcessorState used to parse the match-patterns and
* use-expressions
*/
ProcessorState* mProcessorState;
/*
* Used to return empty nodeset
*/
NodeSet mEmptyNodeset;
};
/**
* The definition for the XSLT format-number() function
**/

Просмотреть файл

@ -435,9 +435,10 @@ MBool txKeyPattern::matches(Node* aNode, txIMatchContext* aContext)
contextDoc = (Document*)aNode;
else
contextDoc = aNode->getOwnerDocument();
txXSLKey* key = mProcessorState->getKey(mName);
const NodeSet* nodes = key->getNodes(mValue, contextDoc);
if (!nodes || nodes->isEmpty())
const NodeSet* nodes = 0;
nsresult rv = mProcessorState->getKeyNodes(mName, contextDoc, mValue,
PR_TRUE, &nodes);
if (NS_FAILED(rv) || !nodes || nodes->isEmpty())
return MB_FALSE;
MBool isTrue = nodes->contains(aNode);
return isTrue;