/* -*- 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 #include template< typename A, typename D > ScCompressedArray::ScCompressedArray( A nMaxAccessP, const D& rValue ) : nCount(1) , nLimit(1) , pData( new DataEntry[1]) , nMaxAccess( nMaxAccessP) { pData[0].aValue = rValue; pData[0].nEnd = nMaxAccess; } template< typename A, typename D > size_t ScCompressedArray::Search( A nAccess ) const { if (nAccess == 0) return 0; tools::Long nLo = 0; tools::Long nHi = static_cast(nCount) - 1; tools::Long nStart = 0; tools::Long i = 0; bool bFound = (nCount == 1); while (!bFound && nLo <= nHi) { i = (nLo + nHi) / 2; if (i > 0) nStart = static_cast(pData[i - 1].nEnd); else nStart = -1; tools::Long nEnd = static_cast(pData[i].nEnd); if (nEnd < static_cast(nAccess)) nLo = ++i; else if (nStart >= static_cast(nAccess)) nHi = --i; else bFound = true; } return (bFound ? static_cast(i) : (nAccess < 0 ? 0 : nCount-1)); } template< typename A, typename D > void ScCompressedArray::SetValue( A nStart, A nEnd, const D& rValue ) { if (!(0 <= nStart && nStart <= nMaxAccess && 0 <= nEnd && nEnd <= nMaxAccess && nStart <= nEnd)) return; if ((nStart == 0) && (nEnd == nMaxAccess)) Reset( rValue); else { // Create a temporary copy in case we got a reference passed that // points to a part of the array to be reallocated. D aNewVal( rValue); size_t nNeeded = nCount + 2; if (nLimit < nNeeded) { nLimit *= 1.5; if (nLimit < nNeeded) nLimit = nNeeded; std::unique_ptr pNewData(new DataEntry[nLimit]); memcpy( pNewData.get(), pData.get(), nCount*sizeof(DataEntry)); pData = std::move(pNewData); } size_t ni; // number of leading entries size_t nInsert; // insert position (nMaxAccess+1 := no insert) bool bCombined = false; bool bSplit = false; if (nStart > 0) { // skip leading ni = this->Search( nStart); nInsert = nMaxAccess+1; if (!(pData[ni].aValue == aNewVal)) { if (ni == 0 || (pData[ni-1].nEnd < nStart - 1)) { // may be a split or a simple insert or just a shrink, // row adjustment is done further down if (pData[ni].nEnd > nEnd) bSplit = true; ni++; nInsert = ni; } else if (ni > 0 && pData[ni-1].nEnd == nStart - 1) nInsert = ni; } if (ni > 0 && pData[ni-1].aValue == aNewVal) { // combine pData[ni-1].nEnd = nEnd; nInsert = nMaxAccess+1; bCombined = true; } } else { nInsert = 0; ni = 0; } size_t nj = ni; // stop position of range to replace while (nj < nCount && pData[nj].nEnd <= nEnd) nj++; if (!bSplit) { if (nj < nCount && pData[nj].aValue == aNewVal) { // combine if (ni > 0) { if (pData[ni-1].aValue == aNewVal) { // adjacent entries pData[ni-1].nEnd = pData[nj].nEnd; nj++; } else if (ni == nInsert) pData[ni-1].nEnd = nStart - 1; // shrink } nInsert = nMaxAccess+1; bCombined = true; } else if (ni > 0 && ni == nInsert) pData[ni-1].nEnd = nStart - 1; // shrink } if (ni < nj) { // remove middle entries if (!bCombined) { // replace one entry pData[ni].nEnd = nEnd; pData[ni].aValue = aNewVal; ni++; nInsert = nMaxAccess+1; } if (ni < nj) { // remove entries memmove( pData.get() + ni, pData.get() + nj, (nCount - nj) * sizeof(DataEntry)); nCount -= nj - ni; } } if (nInsert < static_cast(nMaxAccess+1)) { // insert or append new entry if (nInsert <= nCount) { if (!bSplit) memmove( pData.get() + nInsert + 1, pData.get() + nInsert, (nCount - nInsert) * sizeof(DataEntry)); else { memmove( pData.get() + nInsert + 2, pData.get() + nInsert, (nCount - nInsert) * sizeof(DataEntry)); pData[nInsert+1] = pData[nInsert-1]; nCount++; } } if (nInsert) pData[nInsert-1].nEnd = nStart - 1; pData[nInsert].nEnd = nEnd; pData[nInsert].aValue = aNewVal; nCount++; } } } template< typename A, typename D > void ScCompressedArray::CopyFrom( const ScCompressedArray& rArray, A nDestStart, A nDestEnd, A nSrcStart ) { assert( this != &rArray && "cannot copy self->self" ); size_t nIndex = 0; A nRegionEnd; for (A j=nDestStart; j<=nDestEnd; ++j) { const D& rValue = (j==nDestStart ? rArray.GetValue( j - nDestStart + nSrcStart, nIndex, nRegionEnd) : rArray.GetNextValue( nIndex, nRegionEnd)); nRegionEnd = nRegionEnd - nSrcStart + nDestStart; if (nRegionEnd > nDestEnd) nRegionEnd = nDestEnd; this->SetValue( j, nRegionEnd, rValue); j = nRegionEnd; } } template< typename A, typename D > const D& ScCompressedArray::Insert( A nStart, size_t nAccessCount ) { size_t nIndex = this->Search( nStart); // No real insertion is needed, simply extend the one entry and adapt all // following. In case nStart points to the start row of an entry, extend // the previous entry (inserting before nStart). if (nIndex > 0 && pData[nIndex-1].nEnd+1 == nStart) --nIndex; const D& rValue = pData[nIndex].aValue; // the value "copied" do { pData[nIndex].nEnd += nAccessCount; if (pData[nIndex].nEnd >= nMaxAccess) { pData[nIndex].nEnd = nMaxAccess; nCount = nIndex + 1; // discard trailing entries } } while (++nIndex < nCount); return rValue; } template< typename A, typename D > void ScCompressedArray::InsertPreservingSize( A nStart, size_t nAccessCount, const D& rFillValue ) { const A nPrevLastPos = GetLastPos(); Insert(nStart, nAccessCount); for (A i = nStart; i < A(nStart + nAccessCount); ++i) SetValue(i, rFillValue); const A nNewLastPos = GetLastPos(); Remove(nPrevLastPos, nNewLastPos - nPrevLastPos); } template< typename A, typename D > void ScCompressedArray::Remove( A nStart, size_t nAccessCount ) { A nEnd = nStart + nAccessCount - 1; size_t nIndex = this->Search( nStart); // equalize/combine/remove all entries in between if (nEnd > pData[nIndex].nEnd) this->SetValue( nStart, nEnd, pData[nIndex].aValue); // remove an exactly matching entry by shifting up all following by one if ((nStart == 0 || (nIndex > 0 && nStart == pData[nIndex-1].nEnd+1)) && pData[nIndex].nEnd == nEnd && nIndex < nCount-1) { // In case removing an entry results in two adjacent entries with // identical data, combine them into one. This is also necessary to // make the algorithm used in SetValue() work correctly, it relies on // the fact that consecutive values actually differ. size_t nRemove; if (nIndex > 0 && pData[nIndex-1].aValue == pData[nIndex+1].aValue) { nRemove = 2; --nIndex; } else nRemove = 1; memmove( pData.get() + nIndex, pData.get() + nIndex + nRemove, (nCount - (nIndex + nRemove)) * sizeof(DataEntry)); nCount -= nRemove; } // adjust end rows, nIndex still being valid do { pData[nIndex].nEnd -= nAccessCount; } while (++nIndex < nCount); pData[nCount-1].nEnd = nMaxAccess; } template< typename A, typename D > void ScCompressedArray::RemovePreservingSize( A nStart, size_t nAccessCount, const D& rFillValue ) { const A nPrevLastPos = GetLastPos(); Remove(nStart, nAccessCount); const A nNewLastPos = GetLastPos(); InsertPreservingSize(nNewLastPos, nNewLastPos - nPrevLastPos, rFillValue); } template< typename A, typename D > void ScCompressedArray::Iterator::operator++() { ++mnRegion; if (mnRegion > mrArray.pData[mnIndex].nEnd) ++mnIndex; } template< typename A, typename D > typename ScCompressedArray::Iterator ScCompressedArray::Iterator::operator+(size_t nAccessCount) const { A nRegion = mnRegion + nAccessCount; auto nIndex = mnIndex; while (nRegion > mrArray.pData[nIndex].nEnd) ++nIndex; return Iterator(mrArray, nIndex, nRegion); } // === ScBitMaskCompressedArray ============================================== template< typename A, typename D > void ScBitMaskCompressedArray::AndValue( A nStart, A nEnd, const D& rValueToAnd ) { if (nStart > nEnd) return; size_t nIndex = this->Search( nStart); do { if ((this->pData[nIndex].aValue & rValueToAnd) != this->pData[nIndex].aValue) { A nS = ::std::max( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart); A nE = ::std::min( this->pData[nIndex].nEnd, nEnd); this->SetValue( nS, nE, this->pData[nIndex].aValue & rValueToAnd); if (nE >= nEnd) break; // while nIndex = this->Search( nE + 1); } else if (this->pData[nIndex].nEnd >= nEnd) break; // while else ++nIndex; } while (nIndex < this->nCount); } template< typename A, typename D > void ScBitMaskCompressedArray::OrValue( A nStart, A nEnd, const D& rValueToOr ) { if (nStart > nEnd) return; size_t nIndex = this->Search( nStart); do { if ((this->pData[nIndex].aValue | rValueToOr) != this->pData[nIndex].aValue) { A nS = ::std::max( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart); A nE = ::std::min( this->pData[nIndex].nEnd, nEnd); this->SetValue( nS, nE, this->pData[nIndex].aValue | rValueToOr); if (nE >= nEnd) break; // while nIndex = this->Search( nE + 1); } else if (this->pData[nIndex].nEnd >= nEnd) break; // while else ++nIndex; } while (nIndex < this->nCount); } template< typename A, typename D > void ScBitMaskCompressedArray::CopyFromAnded( const ScBitMaskCompressedArray& rArray, A nStart, A nEnd, const D& rValueToAnd ) { size_t nIndex = 0; A nRegionEnd; for (A j=nStart; j<=nEnd; ++j) { const D& rValue = (j==nStart ? rArray.GetValue( j, nIndex, nRegionEnd) : rArray.GetNextValue( nIndex, nRegionEnd)); if (nRegionEnd > nEnd) nRegionEnd = nEnd; this->SetValue( j, nRegionEnd, rValue & rValueToAnd); j = nRegionEnd; } } template< typename A, typename D > A ScBitMaskCompressedArray::GetLastAnyBitAccess( const D& rBitMask ) const { A nEnd = ::std::numeric_limits::max(); size_t nIndex = this->nCount-1; while (true) { if (this->pData[nIndex].aValue & rBitMask) { nEnd = this->pData[nIndex].nEnd; break; // while } else { if (nIndex > 0) { --nIndex; if (this->pData[nIndex].nEnd < 0) break; // while } else break; // while } } return nEnd; } // === Force instantiation of specializations ================================ template class ScCompressedArray< SCROW, CRFlags>; // flags, base class template class ScBitMaskCompressedArray< SCROW, CRFlags>; // flags template class ScCompressedArray< SCCOL, sal_uInt16>; template class ScCompressedArray< SCCOL, CRFlags>; template class ScCompressedArray< SCROW, sal_uInt16>; template class ScBitMaskCompressedArray< SCCOL, CRFlags>; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */