diff options
Diffstat (limited to 'sc/source/filter/excel/tokstack.cxx')
-rw-r--r-- | sc/source/filter/excel/tokstack.cxx | 752 |
1 files changed, 752 insertions, 0 deletions
diff --git a/sc/source/filter/excel/tokstack.cxx b/sc/source/filter/excel/tokstack.cxx new file mode 100644 index 000000000..a0e3974ca --- /dev/null +++ b/sc/source/filter/excel/tokstack.cxx @@ -0,0 +1,752 @@ +/* -*- 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 <tokstack.hxx> +#include <scmatrix.hxx> + +#include <svl/sharedstringpool.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <algorithm> +#include <string.h> + +const sal_uInt16 TokenPool::nScTokenOff = 8192; + +TokenStack::TokenStack( ) + : pStack( new TokenId[ nSize ] ) +{ + Reset(); +} + +TokenStack::~TokenStack() +{ +} + +// !ATTENTION!": to the outside the numbering starts with 1! +// !ATTENTION!": SC-Token are stored with an offset nScTokenOff +// -> to distinguish from other tokens + +TokenPool::TokenPool( svl::SharedStringPool& rSPool ) : + mrStringPool(rSPool) +{ + // pool for Id-sequences + nP_Id = 256; + pP_Id.reset( new sal_uInt16[ nP_Id ] ); + + // pool for Ids + nElement = 32; + pElement.reset( new sal_uInt16[ nElement ] ); + pType.reset( new E_TYPE[ nElement ] ); + pSize.reset( new sal_uInt16[ nElement ] ); + nP_IdLast = 0; + + nP_Matrix = 16; + ppP_Matrix.reset( new ScMatrix*[ nP_Matrix ] ); + memset( ppP_Matrix.get(), 0, sizeof( ScMatrix* ) * nP_Matrix ); + + Reset(); +} + +TokenPool::~TokenPool() +{ + ClearMatrix(); +} + +/** Returns the new number of elements, or 0 if overflow. */ +static sal_uInt16 lcl_canGrow( sal_uInt16 nOld ) +{ + if (!nOld) + return 1; + if (nOld == SAL_MAX_UINT16) + return 0; + sal_uInt32 nNew = ::std::max( static_cast<sal_uInt32>(nOld) * 2, + static_cast<sal_uInt32>(nOld) + 1); + if (nNew > SAL_MAX_UINT16) + nNew = SAL_MAX_UINT16; + if (nNew - 1 < nOld) + nNew = 0; + return static_cast<sal_uInt16>(nNew); +} + +bool TokenPool::GrowId() +{ + sal_uInt16 nP_IdNew = lcl_canGrow( nP_Id); + if (!nP_IdNew) + return false; + + sal_uInt16* pP_IdNew = new (::std::nothrow) sal_uInt16[ nP_IdNew ]; + if (!pP_IdNew) + return false; + + for( sal_uInt16 nL = 0 ; nL < nP_Id ; nL++ ) + pP_IdNew[ nL ] = pP_Id[ nL ]; + + nP_Id = nP_IdNew; + + pP_Id.reset( pP_IdNew ); + return true; +} + +bool TokenPool::CheckElementOrGrow() +{ + // Last possible ID to be assigned somewhere is nElementCurrent+1 + if (nElementCurrent + 1 == nScTokenOff - 1) + { + SAL_WARN("sc.filter","TokenPool::CheckElementOrGrow - last possible ID " << nElementCurrent+1); + return false; + } + + if (nElementCurrent >= nElement) + return GrowElement(); + + return true; +} + +bool TokenPool::GrowElement() +{ + sal_uInt16 nElementNew = lcl_canGrow( nElement); + if (!nElementNew) + return false; + + std::unique_ptr<sal_uInt16[]> pElementNew(new (::std::nothrow) sal_uInt16[ nElementNew ]); + std::unique_ptr<E_TYPE[]> pTypeNew(new (::std::nothrow) E_TYPE[ nElementNew ]); + std::unique_ptr<sal_uInt16[]> pSizeNew(new (::std::nothrow) sal_uInt16[ nElementNew ]); + if (!pElementNew || !pTypeNew || !pSizeNew) + { + return false; + } + + for( sal_uInt16 nL = 0 ; nL < nElement ; nL++ ) + { + pElementNew[ nL ] = pElement[ nL ]; + pTypeNew[ nL ] = pType[ nL ]; + pSizeNew[ nL ] = pSize[ nL ]; + } + + nElement = nElementNew; + + pElement = std::move( pElementNew ); + pType = std::move( pTypeNew ); + pSize = std::move( pSizeNew ); + return true; +} + +bool TokenPool::GrowMatrix() +{ + sal_uInt16 nNewSize = lcl_canGrow( nP_Matrix); + if (!nNewSize) + return false; + + ScMatrix** ppNew = new (::std::nothrow) ScMatrix*[ nNewSize ]; + if (!ppNew) + return false; + + memset( ppNew, 0, sizeof( ScMatrix* ) * nNewSize ); + for( sal_uInt16 nL = 0 ; nL < nP_Matrix ; nL++ ) + ppNew[ nL ] = ppP_Matrix[ nL ]; + + ppP_Matrix.reset( ppNew ); + nP_Matrix = nNewSize; + return true; +} + +bool TokenPool::GetElement( const sal_uInt16 nId, ScTokenArray* pScToken ) +{ + if (nId >= nElementCurrent) + { + SAL_WARN("sc.filter","TokenPool::GetElement - Id too large, " << nId << " >= " << nElementCurrent); + return false; + } + + bool bRet = true; + if( pType[ nId ] == T_Id ) + bRet = GetElementRek( nId, pScToken ); + else + { + switch( pType[ nId ] ) + { + case T_Str: + { + sal_uInt16 n = pElement[ nId ]; + auto* p = ppP_Str.getIfInRange( n ); + if (p) + pScToken->AddString(mrStringPool.intern(**p)); + else + bRet = false; + } + break; + case T_D: + { + sal_uInt16 n = pElement[ nId ]; + if (n < pP_Dbl.m_writemark) + pScToken->AddDouble( pP_Dbl[ n ] ); + else + bRet = false; + } + break; + case T_Err: + break; +/* TODO: in case we had FormulaTokenArray::AddError() */ +#if 0 + { + sal_uInt16 n = pElement[ nId ]; + if (n < nP_Err) + pScToken->AddError( pP_Err[ n ] ); + else + bRet = false; + } +#endif + case T_RefC: + { + sal_uInt16 n = pElement[ nId ]; + auto p = ppP_RefTr.getIfInRange(n); + if (p) + pScToken->AddSingleReference( **p ); + else + bRet = false; + } + break; + case T_RefA: + { + sal_uInt16 n = pElement[ nId ]; + if (n < ppP_RefTr.m_writemark && ppP_RefTr[ n ] && n+1 < ppP_RefTr.m_writemark && ppP_RefTr[ n + 1 ]) + { + ScComplexRefData aScComplexRefData; + aScComplexRefData.Ref1 = *ppP_RefTr[ n ]; + aScComplexRefData.Ref2 = *ppP_RefTr[ n + 1 ]; + pScToken->AddDoubleReference( aScComplexRefData ); + } + else + bRet = false; + } + break; + case T_RN: + { + sal_uInt16 n = pElement[nId]; + if (n < maRangeNames.size()) + { + const RangeName& r = maRangeNames[n]; + pScToken->AddRangeName(r.mnIndex, r.mnSheet); + } + } + break; + case T_Ext: + { + sal_uInt16 n = pElement[ nId ]; + auto p = ppP_Ext.getIfInRange(n); + + if( p ) + { + if( (*p)->eId == ocEuroConvert ) + pScToken->AddOpCode( (*p)->eId ); + else + pScToken->AddExternal( (*p)->aText, (*p)->eId ); + } + else + bRet = false; + } + break; + case T_Nlf: + { + sal_uInt16 n = pElement[ nId ]; + auto p = ppP_Nlf.getIfInRange(n); + + if( p ) + pScToken->AddColRowName( **p ); + else + bRet = false; + } + break; + case T_Matrix: + { + sal_uInt16 n = pElement[ nId ]; + ScMatrix* p = ( n < nP_Matrix )? ppP_Matrix[ n ] : nullptr; + + if( p ) + pScToken->AddMatrix( p ); + else + bRet = false; + } + break; + case T_ExtName: + { + sal_uInt16 n = pElement[nId]; + if (n < maExtNames.size()) + { + const ExtName& r = maExtNames[n]; + pScToken->AddExternalName(r.mnFileId, mrStringPool.intern( r.maName)); + } + else + bRet = false; + } + break; + case T_ExtRefC: + { + sal_uInt16 n = pElement[nId]; + if (n < maExtCellRefs.size()) + { + const ExtCellRef& r = maExtCellRefs[n]; + pScToken->AddExternalSingleReference(r.mnFileId, mrStringPool.intern( r.maTabName), r.maRef); + } + else + bRet = false; + } + break; + case T_ExtRefA: + { + sal_uInt16 n = pElement[nId]; + if (n < maExtAreaRefs.size()) + { + const ExtAreaRef& r = maExtAreaRefs[n]; + pScToken->AddExternalDoubleReference(r.mnFileId, mrStringPool.intern( r.maTabName), r.maRef); + } + else + bRet = false; + } + break; + default: + OSL_FAIL("-TokenPool::GetElement(): undefined state!?"); + bRet = false; + } + } + return bRet; +} + +bool TokenPool::GetElementRek( const sal_uInt16 nId, ScTokenArray* pScToken ) +{ +#ifdef DBG_UTIL + m_nRek++; + OSL_ENSURE(m_nRek <= nP_Id, "*TokenPool::GetElement(): recursion loops!?"); +#endif + + OSL_ENSURE( nId < nElementCurrent, "*TokenPool::GetElementRek(): nId >= nElementCurrent" ); + + if (nId >= nElementCurrent) + { + SAL_WARN("sc.filter", "*TokenPool::GetElementRek(): nId >= nElementCurrent"); +#ifdef DBG_UTIL + m_nRek--; +#endif + return false; + } + + if (pType[ nId ] != T_Id) + { + SAL_WARN("sc.filter", "-TokenPool::GetElementRek(): pType[ nId ] != T_Id"); +#ifdef DBG_UTIL + m_nRek--; +#endif + return false; + } + + bool bRet = true; + sal_uInt16 nCnt = pSize[ nId ]; + sal_uInt16 nFirstId = pElement[ nId ]; + if (nFirstId >= nP_Id) + { + SAL_WARN("sc.filter", "TokenPool::GetElementRek: nFirstId >= nP_Id"); + nCnt = 0; + bRet = false; + } + sal_uInt16* pCurrent = nCnt ? &pP_Id[ nFirstId ] : nullptr; + if (nCnt > nP_Id - nFirstId) + { + SAL_WARN("sc.filter", "TokenPool::GetElementRek: nCnt > nP_Id - nFirstId"); + nCnt = nP_Id - nFirstId; + bRet = false; + } + for( ; nCnt > 0 ; nCnt--, pCurrent++ ) + { + assert(pCurrent); + if( *pCurrent < nScTokenOff ) + {// recursion or not? + if (*pCurrent >= nElementCurrent) + { + SAL_WARN("sc.filter", "TokenPool::GetElementRek: *pCurrent >= nElementCurrent"); + bRet = false; + } + else + { + if (pType[ *pCurrent ] == T_Id) + bRet = GetElementRek( *pCurrent, pScToken ); + else + bRet = GetElement( *pCurrent, pScToken ); + } + } + else // elementary SC_Token + pScToken->AddOpCode( static_cast<DefTokenId>( *pCurrent - nScTokenOff ) ); + } + +#ifdef DBG_UTIL + m_nRek--; +#endif + return bRet; +} + +void TokenPool::operator >>( TokenId& rId ) +{ + rId = static_cast<TokenId>( nElementCurrent + 1 ); + + if (!CheckElementOrGrow()) + return; + + pElement[ nElementCurrent ] = nP_IdLast; // Start of Token-sequence + pType[ nElementCurrent ] = T_Id; // set Typeinfo + pSize[ nElementCurrent ] = nP_IdCurrent - nP_IdLast; + // write from nP_IdLast to nP_IdCurrent-1 -> length of the sequence + + nElementCurrent++; // start value for next sequence + nP_IdLast = nP_IdCurrent; +} + +TokenId TokenPool::Store( const double& rDouble ) +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + if( pP_Dbl.m_writemark >= pP_Dbl.m_capacity ) + if (!pP_Dbl.Grow()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[ nElementCurrent ] = pP_Dbl.m_writemark; // Index in Double-Array + pType[ nElementCurrent ] = T_D; // set Typeinfo Double + + pP_Dbl[ pP_Dbl.m_writemark ] = rDouble; + + pSize[ nElementCurrent ] = 1; // does not matter + + nElementCurrent++; + pP_Dbl.m_writemark++; + + return static_cast<const TokenId>(nElementCurrent); // return old value + 1! +} + +TokenId TokenPool::Store( const sal_uInt16 nIndex ) +{ + return StoreName(nIndex, -1); +} + +TokenId TokenPool::Store( const OUString& rString ) +{ + // mostly copied to Store( const char* ), to avoid a temporary string + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + if( ppP_Str.m_writemark >= ppP_Str.m_capacity ) + if (!ppP_Str.Grow()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[ nElementCurrent ] = ppP_Str.m_writemark; // Index in String-Array + pType[ nElementCurrent ] = T_Str; // set Typeinfo String + + // create String + if( !ppP_Str[ ppP_Str.m_writemark ] ) + //...but only, if it does not exist already + ppP_Str[ ppP_Str.m_writemark ].reset( new OUString( rString ) ); + else + //...copy otherwise + *ppP_Str[ ppP_Str.m_writemark ] = rString; + + /* attention truncate to 16 bits */ + pSize[ nElementCurrent ] = static_cast<sal_uInt16>(ppP_Str[ ppP_Str.m_writemark ]->getLength()); + + nElementCurrent++; + ppP_Str.m_writemark++; + + return static_cast<const TokenId>(nElementCurrent); // return old value + 1! +} + +TokenId TokenPool::Store( const ScSingleRefData& rTr ) +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + if( ppP_RefTr.m_writemark >= ppP_RefTr.m_capacity ) + if (!ppP_RefTr.Grow()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[ nElementCurrent ] = ppP_RefTr.m_writemark; + pType[ nElementCurrent ] = T_RefC; // set Typeinfo Cell-Ref + + if( !ppP_RefTr[ ppP_RefTr.m_writemark ] ) + ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr ) ); + else + *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr; + + nElementCurrent++; + ppP_RefTr.m_writemark++; + + return static_cast<const TokenId>(nElementCurrent); // return old value + 1! +} + +TokenId TokenPool::Store( const ScComplexRefData& rTr ) +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + if( ppP_RefTr.m_writemark + 1 >= ppP_RefTr.m_capacity ) + if (!ppP_RefTr.Grow(2)) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[ nElementCurrent ] = ppP_RefTr.m_writemark; + pType[ nElementCurrent ] = T_RefA; // setTypeinfo Area-Ref + + if( !ppP_RefTr[ ppP_RefTr.m_writemark ] ) + ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr.Ref1 ) ); + else + *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr.Ref1; + ppP_RefTr.m_writemark++; + + if( !ppP_RefTr[ ppP_RefTr.m_writemark ] ) + ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr.Ref2 ) ); + else + *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr.Ref2; + ppP_RefTr.m_writemark++; + + nElementCurrent++; + + return static_cast<const TokenId>(nElementCurrent); // return old value + 1! +} + +TokenId TokenPool::Store( const DefTokenId e, const OUString& r ) +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + if( ppP_Ext.m_writemark >= ppP_Ext.m_capacity ) + if (!ppP_Ext.Grow()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[ nElementCurrent ] = ppP_Ext.m_writemark; + pType[ nElementCurrent ] = T_Ext; // set Typeinfo String + + if( ppP_Ext[ ppP_Ext.m_writemark ] ) + { + ppP_Ext[ ppP_Ext.m_writemark ]->eId = e; + ppP_Ext[ ppP_Ext.m_writemark ]->aText = r; + } + else + ppP_Ext[ ppP_Ext.m_writemark ].reset( new EXTCONT( e, r ) ); + + nElementCurrent++; + ppP_Ext.m_writemark++; + + return static_cast<const TokenId>(nElementCurrent); // return old value + 1! +} + +TokenId TokenPool::StoreNlf( const ScSingleRefData& rTr ) +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + if( ppP_Nlf.m_writemark >= ppP_Nlf.m_capacity ) + if (!ppP_Nlf.Grow()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[ nElementCurrent ] = ppP_Nlf.m_writemark; + pType[ nElementCurrent ] = T_Nlf; + + if( ppP_Nlf[ ppP_Nlf.m_writemark ] ) + { + *ppP_Nlf[ ppP_Nlf.m_writemark ] = rTr; + } + else + ppP_Nlf[ ppP_Nlf.m_writemark ].reset( new ScSingleRefData( rTr ) ); + + nElementCurrent++; + ppP_Nlf.m_writemark++; + + return static_cast<const TokenId>(nElementCurrent); +} + +TokenId TokenPool::StoreMatrix() +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + if( nP_MatrixCurrent >= nP_Matrix ) + if (!GrowMatrix()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[ nElementCurrent ] = nP_MatrixCurrent; + pType[ nElementCurrent ] = T_Matrix; + + ScMatrix* pM = new ScMatrix( 0, 0 ); + pM->IncRef( ); + ppP_Matrix[ nP_MatrixCurrent ] = pM; + + nElementCurrent++; + nP_MatrixCurrent++; + + return static_cast<const TokenId>(nElementCurrent); +} + +TokenId TokenPool::StoreName( sal_uInt16 nIndex, sal_Int16 nSheet ) +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[nElementCurrent] = static_cast<sal_uInt16>(maRangeNames.size()); + pType[nElementCurrent] = T_RN; + + maRangeNames.emplace_back(); + RangeName& r = maRangeNames.back(); + r.mnIndex = nIndex; + r.mnSheet = nSheet; + + ++nElementCurrent; + + return static_cast<const TokenId>(nElementCurrent); +} + +TokenId TokenPool::StoreExtName( sal_uInt16 nFileId, const OUString& rName ) +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtNames.size()); + pType[nElementCurrent] = T_ExtName; + + maExtNames.emplace_back(); + ExtName& r = maExtNames.back(); + r.mnFileId = nFileId; + r.maName = rName; + + ++nElementCurrent; + + return static_cast<const TokenId>(nElementCurrent); +} + +TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef ) +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtCellRefs.size()); + pType[nElementCurrent] = T_ExtRefC; + + maExtCellRefs.emplace_back(); + ExtCellRef& r = maExtCellRefs.back(); + r.mnFileId = nFileId; + r.maTabName = rTabName; + r.maRef = rRef; + + ++nElementCurrent; + + return static_cast<const TokenId>(nElementCurrent); +} + +TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef ) +{ + if (!CheckElementOrGrow()) + return static_cast<const TokenId>(nElementCurrent+1); + + pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtAreaRefs.size()); + pType[nElementCurrent] = T_ExtRefA; + + maExtAreaRefs.emplace_back(); + ExtAreaRef& r = maExtAreaRefs.back(); + r.mnFileId = nFileId; + r.maTabName = rTabName; + r.maRef = rRef; + + ++nElementCurrent; + + return static_cast<const TokenId>(nElementCurrent); +} + +void TokenPool::Reset() +{ + nP_IdCurrent = nP_IdLast = nElementCurrent + = ppP_Str.m_writemark = pP_Dbl.m_writemark = pP_Err.m_writemark + = ppP_RefTr.m_writemark = ppP_Ext.m_writemark = ppP_Nlf.m_writemark = nP_MatrixCurrent = 0; + maRangeNames.clear(); + maExtNames.clear(); + maExtCellRefs.clear(); + maExtAreaRefs.clear(); + ClearMatrix(); +} + +bool TokenPool::IsSingleOp( const TokenId& rId, const DefTokenId eId ) const +{ + sal_uInt16 nId = static_cast<sal_uInt16>(rId); + if( nId && nId <= nElementCurrent ) + {// existent? + nId--; + if( T_Id == pType[ nId ] ) + {// Token-Sequence? + if( pSize[ nId ] == 1 ) + {// EXACTLY 1 Token + sal_uInt16 nPid = pElement[ nId ]; + if (nPid < nP_Id) + { + sal_uInt16 nSecId = pP_Id[ nPid ]; + if( nSecId >= nScTokenOff ) + {// Default-Token? + return static_cast<DefTokenId>( nSecId - nScTokenOff ) == eId; // wanted? + } + } + } + } + } + + return false; +} + +const OUString* TokenPool::GetExternal( const TokenId& rId ) const +{ + const OUString* p = nullptr; + sal_uInt16 n = static_cast<sal_uInt16>(rId); + if( n && n <= nElementCurrent ) + { + n--; + if( pType[ n ] == T_Ext ) + { + sal_uInt16 nExt = pElement[ n ]; + if ( nExt < ppP_Ext.m_writemark && ppP_Ext[ nExt ] ) + p = &ppP_Ext[ nExt ]->aText; + } + } + + return p; +} + +ScMatrix* TokenPool::GetMatrix( unsigned int n ) const +{ + if( n < nP_MatrixCurrent ) + return ppP_Matrix[ n ]; + else + SAL_WARN("sc.filter", "GetMatrix: " << n << " >= " << nP_MatrixCurrent); + return nullptr; +} + +void TokenPool::ClearMatrix() +{ + for(sal_uInt16 n = 0 ; n < nP_Matrix ; n++ ) + { + if( ppP_Matrix[ n ] ) + { + ppP_Matrix[ n ]->DecRef( ); + ppP_Matrix[n] = nullptr; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |