Kaydet (Commit) e09cedd9 authored tarafından Kohei Yoshida's avatar Kohei Yoshida

Initial attempt to speed up matrix comparison.

And move the value compare code to somewhere public.  I'll be using it
from the ScMatrix internal.

This change requires mdds 0.9.1 which is yet to be released.

Change-Id: I942133c85b614b3404006fa38af111ace9361fd4
üst 0b9b6685
...@@ -195,6 +195,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\ ...@@ -195,6 +195,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
sc/source/core/tool/chartpos \ sc/source/core/tool/chartpos \
sc/source/core/tool/chgtrack \ sc/source/core/tool/chgtrack \
sc/source/core/tool/chgviset \ sc/source/core/tool/chgviset \
sc/source/core/tool/compare \
sc/source/core/tool/compiler \ sc/source/core/tool/compiler \
sc/source/core/tool/consoli \ sc/source/core/tool/consoli \
sc/source/core/tool/dbdata \ sc/source/core/tool/dbdata \
......
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 .
*/
#ifndef SC_COMPARE_HXX
#define SC_COMPARE_HXX
#include "queryentry.hxx"
#include "rtl/ustring.hxx"
class ScDocument;
namespace sc {
struct Compare
{
double nVal[2];
OUString* pVal[2];
bool bVal[2];
bool bEmpty[2];
Compare( OUString* p1, OUString* p2 )
{
pVal[0] = p1;
pVal[1] = p2;
bEmpty[0] = false;
bEmpty[1] = false;
}
};
struct CompareOptions
{
ScQueryEntry aQueryEntry;
bool bRegEx;
bool bMatchWholeCell;
bool bIgnoreCase;
CompareOptions( ScDocument* pDoc, const ScQueryEntry& rEntry, bool bReg );
private:
// Not implemented, prevent usage.
CompareOptions();
CompareOptions( const CompareOptions & );
CompareOptions& operator=( const CompareOptions & );
};
double CompareFunc( bool bIgnoreCase, const Compare& rComp, CompareOptions* pOptions = NULL );
}
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -25,8 +25,10 @@ ...@@ -25,8 +25,10 @@
#include "formula/errorcodes.hxx" #include "formula/errorcodes.hxx"
#include "scdllapi.h" #include "scdllapi.h"
#include <rtl/ustring.hxx> #include <rtl/ustring.hxx>
#include "svl/sharedstring.hxx"
#include <boost/intrusive_ptr.hpp> #include <boost/intrusive_ptr.hpp>
#include <boost/interprocess/smart_ptr/unique_ptr.hpp>
#define DEBUG_MATRIX 0 #define DEBUG_MATRIX 0
...@@ -34,8 +36,6 @@ class ScInterpreter; ...@@ -34,8 +36,6 @@ class ScInterpreter;
class SvNumberFormatter; class SvNumberFormatter;
class ScMatrixImpl; class ScMatrixImpl;
namespace svl { class SharedString; }
/** /**
* Try NOT to use this struct. This struct should go away in a hopefully * Try NOT to use this struct. This struct should go away in a hopefully
* not so distant futture. * not so distant futture.
...@@ -43,11 +43,11 @@ namespace svl { class SharedString; } ...@@ -43,11 +43,11 @@ namespace svl { class SharedString; }
struct ScMatrixValue struct ScMatrixValue
{ {
double fVal; double fVal;
OUString aStr; svl::SharedString aStr;
ScMatValType nType; ScMatValType nType;
/// Only valid if ScMatrix methods indicate so! /// Only valid if ScMatrix methods indicate so!
const OUString& GetString() const { return aStr; } OUString GetString() const { return aStr.getString(); }
/// Only valid if ScMatrix methods indicate that this is no string! /// Only valid if ScMatrix methods indicate that this is no string!
sal_uInt16 GetError() const { return GetDoubleErrorValue( fVal); } sal_uInt16 GetError() const { return GetDoubleErrorValue( fVal); }
...@@ -137,6 +137,31 @@ public: ...@@ -137,6 +137,31 @@ public:
mfFirst(r.mfFirst), mfRest(r.mfRest), mnCount(r.mnCount) {} mfFirst(r.mfFirst), mfRest(r.mfRest), mnCount(r.mnCount) {}
}; };
struct Pos;
struct ConstPos;
static void DeletePosition( const Pos* p );
static void DeletePosition( const ConstPos* p );
struct PosDeleter : std::unary_function<const Pos*, void>
{
void operator() (const Pos* p)
{
DeletePosition(p);
}
};
struct ConstPosDeleter : std::unary_function<const ConstPos*, void>
{
void operator() (const ConstPos* p)
{
DeletePosition(p);
}
};
typedef boost::interprocess::unique_ptr<Pos, PosDeleter> PosRef;
typedef boost::interprocess::unique_ptr<ConstPos, PosDeleter> ConstPosRef;
/// The maximum number of elements a matrix may have at runtime. /// The maximum number of elements a matrix may have at runtime.
inline static size_t GetElementsMax() inline static size_t GetElementsMax()
{ {
...@@ -289,6 +314,8 @@ public: ...@@ -289,6 +314,8 @@ public:
/// @ATTENTION: If bString the ScMatrixValue->pS may still be NULL to indicate /// @ATTENTION: If bString the ScMatrixValue->pS may still be NULL to indicate
/// an empty string! /// an empty string!
ScMatrixValue Get( SCSIZE nC, SCSIZE nR) const; ScMatrixValue Get( SCSIZE nC, SCSIZE nR) const;
ScMatrixValue Get( const Pos& rPos ) const;
ScMatrixValue Get( const ConstPos& rPos ) const;
/// @return <TRUE/> if string or empty or empty path, in fact non-value. /// @return <TRUE/> if string or empty or empty path, in fact non-value.
sal_Bool IsString( SCSIZE nIndex ) const; sal_Bool IsString( SCSIZE nIndex ) const;
...@@ -317,6 +344,31 @@ public: ...@@ -317,6 +344,31 @@ public:
/// @return <TRUE/> if entire matrix is numeric, including booleans, with no strings or empties /// @return <TRUE/> if entire matrix is numeric, including booleans, with no strings or empties
sal_Bool IsNumeric() const; sal_Bool IsNumeric() const;
Pos* GetPosition( size_t nC, size_t nR );
ConstPos* GetConstPosition( size_t nC, size_t nR ) const;
bool NextPosition( Pos& rPos );
bool NextPosition( ConstPos& rPos ) const;
bool IsValue( const Pos& rPos ) const;
bool IsValue( const ConstPos& rPos ) const;
bool IsEmpty( const Pos& rPos ) const;
bool IsEmpty( const ConstPos& rPos ) const;
double GetDouble( const Pos& rPos ) const;
double GetDouble( const ConstPos& rPos ) const;
svl::SharedString GetString( const Pos& rPos ) const;
svl::SharedString GetString( const ConstPos& rPos ) const;
void PutDouble( double fVal, Pos& rPos );
void PutString( const svl::SharedString& rStr, Pos& rPos );
void PutEmpty( Pos& rPos );
void PutEmptyPath( Pos& rPos );
void PutError( sal_uInt16 nErr, Pos& rPos );
void PutBoolean( bool bVal, Pos& rPos );
void MatTrans( ScMatrix& mRes) const; void MatTrans( ScMatrix& mRes) const;
void MatCopy ( ScMatrix& mRes) const; void MatCopy ( ScMatrix& mRes) const;
......
...@@ -1458,7 +1458,7 @@ struct PartiallyFilledEmptyMatrix ...@@ -1458,7 +1458,7 @@ struct PartiallyFilledEmptyMatrix
else if (nCol == 8 && nRow == 2) else if (nCol == 8 && nRow == 2)
{ {
CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_STRING); CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_STRING);
CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.aStr == "Test"); CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.aStr.getString() == "Test");
} }
else if (nCol == 8 && nRow == 11) else if (nCol == 8 && nRow == 11)
{ {
......
...@@ -42,8 +42,6 @@ struct ScQueryParam; ...@@ -42,8 +42,6 @@ struct ScQueryParam;
struct ScDBQueryParamBase; struct ScDBQueryParamBase;
struct ScQueryEntry; struct ScQueryEntry;
struct ScCompare;
struct ScCompareOptions;
struct ScSingleRefData; struct ScSingleRefData;
struct ScComplexRefData; struct ScComplexRefData;
...@@ -54,6 +52,8 @@ struct ScRefCellValue; ...@@ -54,6 +52,8 @@ struct ScRefCellValue;
namespace sc { namespace sc {
struct RangeMatrix; struct RangeMatrix;
struct Compare;
struct CompareOptions;
} }
...@@ -382,16 +382,12 @@ void ScChoseJump(); ...@@ -382,16 +382,12 @@ void ScChoseJump();
// Returns true if last jump was executed and result matrix pushed. // Returns true if last jump was executed and result matrix pushed.
bool JumpMatrix( short nStackLevel ); bool JumpMatrix( short nStackLevel );
/** @param pOptions
NULL means case sensitivity document option is to be used!
*/
double CompareFunc( const ScCompare& rComp, ScCompareOptions* pOptions = NULL );
double Compare(); double Compare();
/** @param pOptions /** @param pOptions
NULL means case sensitivity document option is to be used! NULL means case sensitivity document option is to be used!
*/ */
sc::RangeMatrix CompareMat( ScCompareOptions* pOptions = NULL ); sc::RangeMatrix CompareMat( sc::CompareOptions* pOptions = NULL );
ScMatrixRef QueryMat( const ScMatrixRef& pMat, ScCompareOptions& rOptions ); ScMatrixRef QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions );
void ScEqual(); void ScEqual();
void ScNotEqual(); void ScNotEqual();
void ScLess(); void ScLess();
......
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 .
*/
#include "compare.hxx"
#include "document.hxx"
#include "docoptio.hxx"
#include "unotools/textsearch.hxx"
namespace sc {
CompareOptions::CompareOptions( ScDocument* pDoc, const ScQueryEntry& rEntry, bool bReg ) :
aQueryEntry(rEntry),
bRegEx(bReg),
bMatchWholeCell(pDoc->GetDocOptions().IsMatchWholeCell()),
bIgnoreCase(true)
{
bRegEx = (bRegEx && (aQueryEntry.eOp == SC_EQUAL || aQueryEntry.eOp == SC_NOT_EQUAL));
// Interpreter functions usually are case insensitive, except the simple
// comparison operators, for which these options aren't used. Override in
// struct if needed.
}
/** @param pOptions
NULL means case sensitivity document option is to be used!
*/
double CompareFunc( bool bIgnoreCase, const Compare& rComp, CompareOptions* pOptions )
{
// Keep DoubleError if encountered
// #i40539# if bEmpty is set, bVal/nVal are uninitialized
if ( !rComp.bEmpty[0] && rComp.bVal[0] && !::rtl::math::isFinite( rComp.nVal[0]))
return rComp.nVal[0];
if ( !rComp.bEmpty[1] && rComp.bVal[1] && !::rtl::math::isFinite( rComp.nVal[1]))
return rComp.nVal[1];
size_t nStringQuery = 0; // 0:=no, 1:=0, 2:=1
double fRes = 0;
if ( rComp.bEmpty[ 0 ] )
{
if ( rComp.bEmpty[ 1 ] )
; // empty cell == empty cell, fRes 0
else if( rComp.bVal[ 1 ] )
{
if ( !::rtl::math::approxEqual( rComp.nVal[ 1 ], 0.0 ) )
{
if ( rComp.nVal[ 1 ] < 0.0 )
fRes = 1; // empty cell > -x
else
fRes = -1; // empty cell < x
}
// else: empty cell == 0.0
}
else
{
if ( !rComp.pVal[ 1 ]->isEmpty() )
fRes = -1; // empty cell < "..."
// else: empty cell == ""
}
}
else if ( rComp.bEmpty[ 1 ] )
{
if( rComp.bVal[ 0 ] )
{
if ( !::rtl::math::approxEqual( rComp.nVal[ 0 ], 0.0 ) )
{
if ( rComp.nVal[ 0 ] < 0.0 )
fRes = -1; // -x < empty cell
else
fRes = 1; // x > empty cell
}
// else: empty cell == 0.0
}
else
{
if ( !rComp.pVal[ 0 ]->isEmpty() )
fRes = 1; // "..." > empty cell
// else: "" == empty cell
}
}
else if( rComp.bVal[ 0 ] )
{
if( rComp.bVal[ 1 ] )
{
if ( !::rtl::math::approxEqual( rComp.nVal[ 0 ], rComp.nVal[ 1 ] ) )
{
if( rComp.nVal[ 0 ] - rComp.nVal[ 1 ] < 0 )
fRes = -1;
else
fRes = 1;
}
}
else
{
fRes = -1; // number is less than string
nStringQuery = 2; // 1+1
}
}
else if( rComp.bVal[ 1 ] )
{
fRes = 1; // string is greater than number
nStringQuery = 1; // 0+1
}
else
{
// Both strings.
if (pOptions)
{
// All similar to ScTable::ValidQuery(), *rComp.pVal[1] actually
// is/must be identical to *rEntry.pStr, which is essential for
// regex to work through GetSearchTextPtr().
ScQueryEntry& rEntry = pOptions->aQueryEntry;
OSL_ENSURE(rEntry.GetQueryItem().maString.getString().equals(*rComp.pVal[1]), "ScInterpreter::CompareFunc: broken options");
if (pOptions->bRegEx)
{
sal_Int32 nStart = 0;
sal_Int32 nStop = rComp.pVal[0]->getLength();
bool bMatch = rEntry.GetSearchTextPtr(
!pOptions->bIgnoreCase)->SearchForward( *rComp.pVal[0],
&nStart, &nStop);
if (bMatch && pOptions->bMatchWholeCell && (nStart != 0 || nStop != rComp.pVal[0]->getLength()))
bMatch = false; // RegEx must match entire string.
fRes = (bMatch ? 0 : 1);
}
else if (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
{
::utl::TransliterationWrapper* pTransliteration =
(pOptions->bIgnoreCase ? ScGlobal::GetpTransliteration() :
ScGlobal::GetCaseTransliteration());
bool bMatch;
if (pOptions->bMatchWholeCell)
bMatch = pTransliteration->isEqual( *rComp.pVal[0], *rComp.pVal[1]);
else
{
OUString aCell( pTransliteration->transliterate(
*rComp.pVal[0], ScGlobal::eLnge, 0,
rComp.pVal[0]->getLength(), NULL));
OUString aQuer( pTransliteration->transliterate(
*rComp.pVal[1], ScGlobal::eLnge, 0,
rComp.pVal[1]->getLength(), NULL));
bMatch = (aCell.indexOf( aQuer ) != -1);
}
fRes = (bMatch ? 0 : 1);
}
else if (pOptions->bIgnoreCase)
fRes = (double) ScGlobal::GetCollator()->compareString(
*rComp.pVal[ 0 ], *rComp.pVal[ 1 ] );
else
fRes = (double) ScGlobal::GetCaseCollator()->compareString(
*rComp.pVal[ 0 ], *rComp.pVal[ 1 ] );
}
else if (bIgnoreCase)
fRes = (double) ScGlobal::GetCollator()->compareString(
*rComp.pVal[ 0 ], *rComp.pVal[ 1 ] );
else
fRes = (double) ScGlobal::GetCaseCollator()->compareString(
*rComp.pVal[ 0 ], *rComp.pVal[ 1 ] );
}
if (nStringQuery && pOptions)
{
const ScQueryEntry& rEntry = pOptions->aQueryEntry;
const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
if (!rItems.empty())
{
const ScQueryEntry::Item& rItem = rItems[0];
if (rItem.meType != ScQueryEntry::ByString && !rItem.maString.isEmpty() &&
(rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL))
{
// As in ScTable::ValidQuery() match a numeric string for a
// number query that originated from a string, e.g. in SUMIF
// and COUNTIF. Transliteration is not needed here.
bool bEqual = (*rComp.pVal[nStringQuery-1]) == rItem.maString.getString();
// match => fRes=0, else fRes=1
fRes = (rEntry.eOp == SC_NOT_EQUAL) ? bEqual : !bEqual;
}
}
}
return fRes;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment