Avoid potential negative array index access to cached text.
[LibreOffice.git] / tools / source / memtools / multisel.cxx
blob739d2874875a02e5dae8327d9baa63c04d4b43ec
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <cstddef>
24 #include <o3tl/string_view.hxx>
25 #include <tools/debug.hxx>
26 #include <tools/multisel.hxx>
28 #include <rtl/ustrbuf.hxx>
30 void MultiSelection::ImplClear()
32 // no selected indexes
33 nSelCount = 0;
34 aSels.clear();
37 std::size_t MultiSelection::ImplFindSubSelection( sal_Int32 nIndex ) const
39 // iterate through the sub selections
40 std::size_t n = 0;
41 for ( ;
42 n < aSels.size() && nIndex > aSels[ n ].Max();
43 ++n ) {} /* empty loop */
44 return n;
47 void MultiSelection::ImplMergeSubSelections( sal_Int32 nPos1, std::size_t nPos2 )
49 // didn't a sub selection at nPos2 exist?
50 if ( nPos2 >= aSels.size() )
51 return;
53 // did the sub selections touch each other?
54 if ( (aSels[ nPos1 ].Max() + 1) == aSels[ nPos2 ].Min() )
56 // merge them
57 aSels[ nPos1 ].Max() = aSels[ nPos2 ].Max();
58 aSels.erase( aSels.begin() + nPos2 );
62 MultiSelection::MultiSelection():
63 aTotRange( 0, -1 ),
64 nCurSubSel(0),
65 nCurIndex(0),
66 nSelCount(0),
67 bCurValid(false)
71 void MultiSelection::Reset()
73 aTotRange = Range(0, -1);
74 bCurValid = false;
75 // clear the old sub selections
76 ImplClear();
79 MultiSelection::MultiSelection( const MultiSelection& rOrig ) :
80 aTotRange(rOrig.aTotRange),
81 nSelCount(rOrig.nSelCount),
82 bCurValid(rOrig.bCurValid)
84 if ( bCurValid )
86 nCurSubSel = rOrig.nCurSubSel;
87 nCurIndex = rOrig.nCurIndex;
89 else
91 nCurSubSel = 0;
92 nCurIndex = 0;
95 // copy the sub selections
96 aSels.insert( aSels.end(), rOrig.aSels.begin(), rOrig.aSels.end() );
99 MultiSelection::MultiSelection( const Range& rRange ):
100 aTotRange(rRange),
101 nCurSubSel(0),
102 nCurIndex(0),
103 nSelCount(0),
104 bCurValid(false)
108 MultiSelection::~MultiSelection()
112 MultiSelection& MultiSelection::operator= ( const MultiSelection& rOrig )
114 aTotRange = rOrig.aTotRange;
115 bCurValid = rOrig.bCurValid;
116 if ( bCurValid )
118 nCurSubSel = rOrig.nCurSubSel;
119 nCurIndex = rOrig.nCurIndex;
122 // clear the old and copy the sub selections
123 ImplClear();
124 aSels.insert( aSels.end(), rOrig.aSels.begin(), rOrig.aSels.end() );
125 nSelCount = rOrig.nSelCount;
127 return *this;
130 void MultiSelection::SelectAll( bool bSelect )
132 ImplClear();
133 if ( bSelect )
135 aSels.push_back( aTotRange );
136 nSelCount = aTotRange.Len();
140 bool MultiSelection::Select( sal_Int32 nIndex, bool bSelect )
142 DBG_ASSERT( aTotRange.Contains(nIndex), "selected index out of range" );
144 // out of range?
145 if ( !aTotRange.Contains(nIndex) )
146 return false;
148 // find the virtual target position
149 std::size_t nSubSelPos = ImplFindSubSelection( nIndex );
151 if ( bSelect )
153 // is it included in the found sub selection?
154 if ( nSubSelPos < aSels.size() && aSels[ nSubSelPos ].Contains( nIndex ) )
155 // already selected, nothing to do
156 return false;
158 // it will become selected
159 ++nSelCount;
161 // is it at the end of the previous sub selection
162 if ( nSubSelPos > 0 &&
163 aSels[ nSubSelPos-1 ].Max() == (nIndex-1) )
165 // expand the previous sub selection
166 aSels[ nSubSelPos-1 ].Max() = nIndex;
168 // try to merge the previous sub selection
169 ImplMergeSubSelections( nSubSelPos-1, nSubSelPos );
171 // is it at the beginning of the found sub selection
172 else if ( nSubSelPos < aSels.size()
173 && aSels[ nSubSelPos ].Min() == (nIndex+1)
175 // expand the found sub selection
176 aSels[ nSubSelPos ].Min() = nIndex;
177 else
179 // create a new sub selection
180 if ( nSubSelPos < aSels.size() ) {
181 aSels.insert( aSels.begin() + nSubSelPos, Range( nIndex, nIndex ) );
182 } else {
183 aSels.push_back( Range( nIndex, nIndex ) );
185 if ( bCurValid && nCurSubSel >= nSubSelPos )
186 ++nCurSubSel;
189 else
191 // is it excluded from the found sub selection?
192 if ( nSubSelPos >= aSels.size()
193 || !aSels[ nSubSelPos ].Contains( nIndex )
195 // not selected, nothing to do
196 return false;
199 // it will become deselected
200 --nSelCount;
202 // is it the only index in the found sub selection?
203 if ( aSels[ nSubSelPos ].Len() == 1 )
205 // remove the complete sub selection
206 aSels.erase( aSels.begin() + nSubSelPos );
207 return true;
210 // is it at the beginning of the found sub selection?
211 if ( aSels[ nSubSelPos ].Min() == nIndex )
212 ++aSels[ nSubSelPos ].Min();
213 // is it at the end of the found sub selection?
214 else if ( aSels[ nSubSelPos ].Max() == nIndex )
215 --aSels[ nSubSelPos ].Max();
216 // it is in the middle of the found sub selection?
217 else
219 // split the sub selection
220 if ( nSubSelPos < aSels.size() ) {
221 aSels.insert( aSels.begin() + nSubSelPos, Range( aSels[ nSubSelPos ].Min(), nIndex-1 ) );
222 } else {
223 aSels.push_back( Range( aSels[ nSubSelPos ].Min(), nIndex-1 ) );
225 aSels[ nSubSelPos+1 ].Min() = nIndex + 1;
229 return true;
232 void MultiSelection::Select( const Range& rIndexRange, bool bSelect )
234 sal_Int32 nOld;
236 sal_Int32 nTmpMin = rIndexRange.Min();
237 sal_Int32 nTmpMax = rIndexRange.Max();
238 sal_Int32 nCurMin = FirstSelected();
239 sal_Int32 nCurMax = LastSelected();
240 DBG_ASSERT(aTotRange.Contains(nTmpMax), "selected index out of range" );
241 DBG_ASSERT(aTotRange.Contains(nTmpMin), "selected index out of range" );
243 // replace whole selection?
244 if( aSels.empty() || (nTmpMin <= nCurMin && nTmpMax >= nCurMax ) )
246 ImplClear();
247 if ( bSelect )
249 aSels.push_back( rIndexRange );
250 nSelCount = rIndexRange.Len();
252 return;
254 // expand on left side?
255 if( nTmpMax < nCurMin )
257 if( bSelect )
259 // extend first range?
260 if( nCurMin > (nTmpMax+1) )
262 aSels.insert( aSels.begin(), rIndexRange );
263 nSelCount += rIndexRange.Len();
265 else
267 auto & rRange = aSels.front();
268 nOld = rRange.Min();
269 rRange.Min() = nTmpMin;
270 nSelCount += ( nOld - nTmpMin );
272 bCurValid = false;
274 return;
276 // expand on right side?
277 else if( nTmpMin > nCurMax )
279 if( bSelect )
281 // extend last range?
282 if( nTmpMin > (nCurMax+1) )
284 aSels.push_back( rIndexRange );
285 nSelCount += rIndexRange.Len();
287 else
289 auto & rRange = aSels.back();
290 nOld = rRange.Max();
291 rRange.Max() = nTmpMax;
292 nSelCount += ( nTmpMax - nOld );
294 bCurValid = false;
296 return;
299 // TODO here is potential for optimization
300 while( nTmpMin <= nTmpMax )
302 Select( nTmpMin, bSelect );
303 nTmpMin++;
307 bool MultiSelection::IsSelected( sal_Int32 nIndex ) const
309 // find the virtual target position
310 std::size_t nSubSelPos = ImplFindSubSelection( nIndex );
312 return nSubSelPos < aSels.size() && aSels[ nSubSelPos ].Contains(nIndex);
315 void MultiSelection::Insert( sal_Int32 nIndex, sal_Int32 nCount )
317 // find the virtual target position
318 std::size_t nSubSelPos = ImplFindSubSelection( nIndex );
320 // did we need to shift the sub selections?
321 if ( nSubSelPos < aSels.size() )
322 { // did we insert an unselected into an existing sub selection?
323 if ( aSels[ nSubSelPos ].Min() != nIndex
324 && aSels[ nSubSelPos ].Contains(nIndex)
325 ) { // split the sub selection
326 if ( nSubSelPos < aSels.size() ) {
327 aSels.insert( aSels.begin() + nSubSelPos, Range( aSels[ nSubSelPos ].Min(), nIndex-1 ) );
328 } else {
329 aSels.push_back( Range( aSels[ nSubSelPos ].Min(), nIndex-1 ) );
331 ++nSubSelPos;
332 aSels[ nSubSelPos ].Min() = nIndex;
335 // shift the sub selections behind the inserting position
336 for ( std::size_t nPos = nSubSelPos; nPos < aSels.size(); ++nPos )
338 aSels[ nPos ].Min() += nCount;
339 aSels[ nPos ].Max() += nCount;
343 bCurValid = false;
344 aTotRange.Max() += nCount;
347 void MultiSelection::Remove( sal_Int32 nIndex )
349 // find the virtual target position
350 std::size_t nSubSelPos = ImplFindSubSelection( nIndex );
352 // did we remove from an existing sub selection?
353 if ( nSubSelPos < aSels.size()
354 && aSels[ nSubSelPos ].Contains(nIndex)
356 // does this sub selection only contain the index to be deleted
357 if ( aSels[ nSubSelPos ].Len() == 1 ) {
358 // completely remove the sub selection
359 aSels.erase( aSels.begin() + nSubSelPos );
360 } else {
361 // shorten this sub selection
362 --( aSels[ nSubSelPos++ ].Max() );
365 // adjust the selected counter
366 --nSelCount;
369 // shift the sub selections behind the removed index
370 for ( std::size_t nPos = nSubSelPos; nPos < aSels.size(); ++nPos )
372 --( aSels[ nPos ].Min() );
373 --( aSels[ nPos ].Max() );
376 bCurValid = false;
377 aTotRange.Max() -= 1;
380 sal_Int32 MultiSelection::FirstSelected()
382 nCurSubSel = 0;
384 bCurValid = !aSels.empty();
385 if ( !bCurValid )
386 return SFX_ENDOFSELECTION;
388 nCurIndex = aSels[ 0 ].Min();
389 return nCurIndex;
392 sal_Int32 MultiSelection::LastSelected()
394 bCurValid = !aSels.empty();
396 if ( !bCurValid )
397 return SFX_ENDOFSELECTION;
399 nCurSubSel = aSels.size() - 1;
400 nCurIndex = aSels[ nCurSubSel ].Max();
401 return nCurIndex;
404 sal_Int32 MultiSelection::NextSelected()
406 if ( !bCurValid )
407 return SFX_ENDOFSELECTION;
409 // is the next index in the current sub selection too?
410 if ( nCurIndex < aSels[ nCurSubSel ].Max() )
411 return ++nCurIndex;
413 // are there further sub selections?
414 if ( ++nCurSubSel >= aSels.size() )
415 // we are at the end!
416 return SFX_ENDOFSELECTION;
418 nCurIndex = aSels[ nCurSubSel ].Min();
419 return nCurIndex;
422 void MultiSelection::SetTotalRange( const Range& rTotRange )
424 aTotRange = rTotRange;
426 // adjust lower boundary
427 Range* pRange = aSels.empty() ? nullptr : &aSels.front();
428 while( pRange )
430 if( pRange->Max() < aTotRange.Min() )
432 aSels.erase( aSels.begin() );
434 else if( pRange->Min() < aTotRange.Min() )
436 pRange->Min() = aTotRange.Min();
437 break;
439 else
440 break;
442 pRange = aSels.empty() ? nullptr : &aSels.front();
445 // adjust upper boundary
446 sal_Int32 nCount = aSels.size();
447 while( nCount )
449 pRange = &aSels[ nCount - 1 ];
450 if( pRange->Min() > aTotRange.Max() )
452 aSels.pop_back();
454 else if( pRange->Max() > aTotRange.Max() )
456 pRange->Max() = aTotRange.Max();
457 break;
459 else
460 break;
462 nCount = aSels.size();
465 // re-calculate selection count
466 nSelCount = 0;
467 for (Range const & rSel : aSels)
468 nSelCount += rSel.Len();
470 bCurValid = false;
471 nCurIndex = 0;
474 // StringRangeEnumerator
476 StringRangeEnumerator::StringRangeEnumerator( std::u16string_view i_rInput,
477 sal_Int32 i_nMinNumber,
478 sal_Int32 i_nMaxNumber,
479 sal_Int32 i_nLogicalOffset
481 : mnCount( 0 )
482 , mnMin( i_nMinNumber )
483 , mnMax( i_nMaxNumber )
484 , mnOffset( i_nLogicalOffset )
485 , mbValidInput( false )
487 // Parse string only if boundaries are valid.
488 if( mnMin >= 0 && mnMax >= 0 && mnMin <= mnMax )
489 mbValidInput = setRange( i_rInput );
492 bool StringRangeEnumerator::checkValue( sal_Int32 i_nValue, const o3tl::sorted_vector< sal_Int32 >* i_pPossibleValues ) const
494 if( i_nValue < 0 || i_nValue < mnMin || i_nValue > mnMax )
495 return false;
496 if( i_pPossibleValues && i_pPossibleValues->find( i_nValue ) == i_pPossibleValues->end() )
497 return false;
498 return true;
501 bool StringRangeEnumerator::insertRange( sal_Int32 i_nFirst, sal_Int32 i_nLast, bool bSequence )
503 bool bSuccess = true;
504 if( bSequence )
506 // Check if the range is completely outside of possible pages range
507 if ((i_nFirst < mnMin && i_nLast < mnMin) ||
508 (i_nFirst > mnMax && i_nLast > mnMax))
509 return false;
510 if( i_nFirst < mnMin )
511 i_nFirst = mnMin;
512 if( i_nFirst > mnMax )
513 i_nFirst = mnMax;
514 if( i_nLast < mnMin )
515 i_nLast = mnMin;
516 if( i_nLast > mnMax )
517 i_nLast = mnMax;
518 if( checkValue( i_nFirst ) && checkValue( i_nLast ) )
520 maSequence.push_back( Range( i_nFirst, i_nLast ) );
521 sal_Int32 nNumber = i_nLast - i_nFirst;
522 nNumber = nNumber < 0 ? -nNumber : nNumber;
523 mnCount += nNumber + 1;
525 else
526 bSuccess = false;
528 else
530 if( checkValue( i_nFirst ) )
532 maSequence.push_back( Range( i_nFirst, i_nFirst ) );
533 mnCount++;
535 else if( checkValue( i_nLast ) )
537 maSequence.push_back( Range( i_nLast, i_nLast ) );
538 mnCount++;
540 else
541 bSuccess = false;
544 return bSuccess;
547 void StringRangeEnumerator::insertJoinedRanges(
548 const std::vector< sal_Int32 >& rNumbers )
550 size_t nCount = rNumbers.size();
551 if( nCount == 0 )
552 return;
554 if( nCount == 1 )
556 insertRange( rNumbers[0], -1, false );
557 return;
560 for( size_t i = 0; i < nCount - 1; i++ )
562 sal_Int32 nFirst = rNumbers[i];
563 sal_Int32 nLast = rNumbers[i + 1];
564 if( i > 0 )
566 if ( nFirst > nLast ) nFirst--;
567 else if( nFirst < nLast ) nFirst++;
570 insertRange( nFirst, nLast, nFirst != nLast );
574 bool StringRangeEnumerator::setRange( std::u16string_view aNewRange )
576 mnCount = 0;
577 maSequence.clear();
579 auto pInput = aNewRange.begin();
580 auto pInputEnd = aNewRange.end();
581 OUStringBuffer aNumberBuf( 16 );
582 std::vector< sal_Int32 > aNumbers;
583 bool bSequence = false;
584 while( pInput != pInputEnd )
586 while( pInput != pInputEnd && *pInput >= '0' && *pInput <= '9' )
587 aNumberBuf.append( *pInput++ );
588 if( !aNumberBuf.isEmpty() )
590 sal_Int32 nNumber = o3tl::toInt32(aNumberBuf) + mnOffset;
591 aNumberBuf.setLength(0);
592 aNumbers.push_back( nNumber );
593 bSequence = false;
595 if (pInput == pInputEnd)
596 break;
597 if( *pInput == '-' )
599 bSequence = true;
600 if( aNumbers.empty() )
602 // push out-of-range small value, to exclude ranges totally outside of possible range
603 aNumbers.push_back( mnMin-1 );
606 else if( *pInput == ',' || *pInput == ';' )
608 if( bSequence && !aNumbers.empty() )
610 // push out-of-range large value, to exclude ranges totally outside of possible range
611 aNumbers.push_back( mnMax+1 );
613 insertJoinedRanges( aNumbers );
615 aNumbers.clear();
616 bSequence = false;
618 else if( *pInput != ' ' )
619 return false; // parse error
621 pInput++;
623 // insert last entries
624 if( bSequence && !aNumbers.empty() )
626 // push out-of-range large value, to exclude ranges totally outside of possible range
627 aNumbers.push_back( mnMax+1 );
629 insertJoinedRanges( aNumbers );
631 return true;
634 bool StringRangeEnumerator::hasValue( sal_Int32 i_nValue, const o3tl::sorted_vector< sal_Int32 >* i_pPossibleValues ) const
636 if( i_pPossibleValues && i_pPossibleValues->find( i_nValue ) == i_pPossibleValues->end() )
637 return false;
638 size_t n = maSequence.size();
639 for( size_t i= 0; i < n; ++i )
641 const StringRangeEnumerator::Range rRange( maSequence[i] );
642 if( rRange.nFirst < rRange.nLast )
644 if( i_nValue >= rRange.nFirst && i_nValue <= rRange.nLast )
645 return true;
647 else
649 if( i_nValue >= rRange.nLast && i_nValue <= rRange.nFirst )
650 return true;
653 return false;
656 StringRangeEnumerator::Iterator& StringRangeEnumerator::Iterator::operator++()
658 if( nRangeIndex >= 0 && nCurrent >= 0 && pEnumerator )
660 const StringRangeEnumerator::Range& rRange( pEnumerator->maSequence[nRangeIndex] );
661 bool bRangeChange = false;
662 if( rRange.nLast < rRange.nFirst )
664 // backward range
665 if( nCurrent > rRange.nLast )
666 nCurrent--;
667 else
668 bRangeChange = true;
670 else
672 // forward range
673 if( nCurrent < rRange.nLast )
674 nCurrent++;
675 else
676 bRangeChange = true;
678 if( bRangeChange )
680 nRangeIndex++;
681 if( size_t(nRangeIndex) == pEnumerator->maSequence.size() )
683 // reached the end
684 nRangeIndex = nCurrent = -1;
686 else
687 nCurrent = pEnumerator->maSequence[nRangeIndex].nFirst;
689 if( nRangeIndex != -1 && nCurrent != -1 )
691 if( ! pEnumerator->checkValue( nCurrent, pPossibleValues ) )
692 return ++(*this);
695 return *this;
699 bool StringRangeEnumerator::Iterator::operator==( const Iterator& i_rCompare ) const
701 return i_rCompare.pEnumerator == pEnumerator && i_rCompare.nRangeIndex == nRangeIndex && i_rCompare.nCurrent == nCurrent;
704 StringRangeEnumerator::Iterator StringRangeEnumerator::begin( const o3tl::sorted_vector< sal_Int32 >* i_pPossibleValues ) const
706 StringRangeEnumerator::Iterator it( this,
707 i_pPossibleValues,
708 maSequence.empty() ? -1 : 0,
709 maSequence.empty() ? -1 : maSequence[0].nFirst );
710 if( ! checkValue(*it, i_pPossibleValues ) )
711 ++it;
712 return it;
715 StringRangeEnumerator::Iterator StringRangeEnumerator::end( const o3tl::sorted_vector< sal_Int32 >* i_pPossibleValues ) const
717 return StringRangeEnumerator::Iterator( this, i_pPossibleValues, -1, -1 );
720 bool StringRangeEnumerator::getRangesFromString( std::u16string_view i_rPageRange,
721 std::vector< sal_Int32 >& o_rPageVector,
722 sal_Int32 i_nMinNumber,
723 sal_Int32 i_nMaxNumber,
724 sal_Int32 i_nLogicalOffset,
725 o3tl::sorted_vector< sal_Int32 > const * i_pPossibleValues
728 o_rPageVector.clear();
730 StringRangeEnumerator aEnum( i_rPageRange, i_nMinNumber, i_nMaxNumber, i_nLogicalOffset ) ;
732 //Even if the input range wasn't completely valid, return what ranges could
733 //be extracted from the input.
734 o_rPageVector.reserve( static_cast< size_t >( aEnum.size() ) );
735 for( StringRangeEnumerator::Iterator it = aEnum.begin( i_pPossibleValues );
736 it != aEnum.end( i_pPossibleValues ); ++it )
738 o_rPageVector.push_back( *it );
741 return aEnum.mbValidInput;
744 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */