1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include <tools/long.hxx>
25 /** Resize block management by this constant.
26 As a result there are approx. 20 * MAXENTRY == 20000 entries available */
27 const sal_uInt16 nBlockGrowSize
= 20;
29 #if OSL_DEBUG_LEVEL > 2
30 #define CHECKIDX( p, n, i, c ) CheckIdx( p, n, i, c );
31 void CheckIdx( BlockInfo
** ppInf
, sal_uInt16 nBlock
, sal_Int32 nSize
, sal_uInt16 nCur
)
33 assert( !nSize
|| nCur
< nBlock
); // BigPtrArray: CurIndex invalid
36 for( sal_uInt16 nCnt
= 0; nCnt
< nBlock
; ++nCnt
, ++ppInf
)
38 nIdx
+= (*ppInf
)->nElem
;
39 // Array with holes is not allowed
40 assert( !nCnt
|| (*(ppInf
-1))->nEnd
+ 1 == (*ppInf
)->nStart
);
42 assert(nIdx
== nSize
); // invalid count in nSize
45 #define CHECKIDX( p, n, i, c )
48 BigPtrArray::BigPtrArray()
50 m_nBlock
= m_nCur
= 0;
52 m_nMaxBlock
= nBlockGrowSize
;
53 m_ppInf
.reset( new BlockInfo
* [ m_nMaxBlock
] );
56 BigPtrArray::~BigPtrArray()
60 BlockInfo
** pp
= m_ppInf
.get();
61 for( sal_uInt16 n
= 0; n
< m_nBlock
; ++n
, ++pp
)
68 // Also moving is done simply here. Optimization is useless because of the
69 // division of this field into multiple parts.
70 void BigPtrArray::Move( sal_Int32 from
, sal_Int32 to
)
74 sal_uInt16 cur
= Index2Block( from
);
75 BlockInfo
* p
= m_ppInf
[ cur
];
76 BigPtrEntry
* pElem
= p
->mvData
[ from
- p
->nStart
];
77 Insert( pElem
, to
); // insert first, then delete!
78 Remove( ( to
< from
) ? ( from
+ 1 ) : from
);
82 BigPtrEntry
* BigPtrArray::operator[]( sal_Int32 idx
) const
84 assert(idx
< m_nSize
); // operator[]: Index out of bounds
85 m_nCur
= Index2Block( idx
);
86 BlockInfo
* p
= m_ppInf
[ m_nCur
];
87 return p
->mvData
[ idx
- p
->nStart
];
90 /** Search a block at a given position */
91 sal_uInt16
BigPtrArray::Index2Block( sal_Int32 pos
) const
94 BlockInfo
* p
= m_ppInf
[ m_nCur
];
95 if( p
->nStart
<= pos
&& p
->nEnd
>= pos
)
102 if( m_nCur
< ( m_nBlock
- 1 ) )
104 p
= m_ppInf
[ m_nCur
+1 ];
105 if( p
->nStart
<= pos
&& p
->nEnd
>= pos
)
109 else if( pos
< p
->nStart
&& m_nCur
> 0 )
111 p
= m_ppInf
[ m_nCur
-1 ];
112 if( p
->nStart
<= pos
&& p
->nEnd
>= pos
)
116 // binary search: always successful
117 sal_uInt16 lower
= 0, upper
= m_nBlock
- 1;
121 sal_uInt16 n
= lower
+ ( upper
- lower
) / 2;
122 cur
= ( n
== cur
) ? n
+1 : n
;
124 if( p
->nStart
<= pos
&& p
->nEnd
>= pos
)
127 if( p
->nStart
> pos
)
134 /** Update all index areas
136 @param pos last correct block (starting point)
138 void BigPtrArray::UpdIndex( sal_uInt16 pos
)
140 BlockInfo
** pp
= m_ppInf
.get() + pos
;
141 sal_Int32 idx
= (*pp
)->nEnd
+ 1;
142 while( ++pos
< m_nBlock
)
144 BlockInfo
* p
= *++pp
;
151 /** Create and insert new block
153 Existing blocks will be moved rearward.
155 @param pos Position at which the new block should be created.
157 BlockInfo
* BigPtrArray::InsBlock( sal_uInt16 pos
)
159 if( m_nBlock
== m_nMaxBlock
)
161 // than extend the array first
162 BlockInfo
** ppNew
= new BlockInfo
* [ m_nMaxBlock
+ nBlockGrowSize
];
163 memcpy( ppNew
, m_ppInf
.get(), m_nMaxBlock
* sizeof( BlockInfo
* ));
164 m_nMaxBlock
+= nBlockGrowSize
;
165 m_ppInf
.reset( ppNew
);
167 if( pos
!= m_nBlock
)
169 memmove( m_ppInf
.get() + pos
+1, m_ppInf
.get() + pos
,
170 ( m_nBlock
- pos
) * sizeof( BlockInfo
* ));
173 BlockInfo
* p
= new BlockInfo
;
177 p
->nStart
= p
->nEnd
= m_ppInf
[ pos
-1 ]->nEnd
+ 1;
179 p
->nStart
= p
->nEnd
= 0;
181 p
->nEnd
--; // no elements
187 void BigPtrArray::BlockDel( sal_uInt16 nDel
)
189 m_nBlock
= m_nBlock
- nDel
;
190 if( m_nMaxBlock
- m_nBlock
> nBlockGrowSize
)
193 nDel
= (( m_nBlock
/ nBlockGrowSize
) + 1 ) * nBlockGrowSize
;
194 BlockInfo
** ppNew
= new BlockInfo
* [ nDel
];
195 memcpy( ppNew
, m_ppInf
.get(), m_nBlock
* sizeof( BlockInfo
* ));
196 m_ppInf
.reset( ppNew
);
201 void BigPtrArray::Insert( BigPtrEntry
* pElem
, sal_Int32 pos
)
203 CHECKIDX( m_ppInf
.get(), m_nBlock
, m_nSize
, m_nCur
);
209 // special case: insert first element
213 else if( pos
== m_nSize
)
215 // special case: insert at end
218 if( p
->nElem
== MAXENTRY
)
219 // the last block is full, create a new one
220 p
= InsBlock( ++cur
);
225 cur
= Index2Block( pos
);
229 if( p
->nElem
== MAXENTRY
)
231 // does the last entry fit into the next block?
233 if( cur
< ( m_nBlock
- 1 ) && m_ppInf
[ cur
+1 ]->nElem
< MAXENTRY
)
235 q
= m_ppInf
[ cur
+1 ];
238 int nCount
= q
->nElem
;
239 auto pFrom
= q
->mvData
.begin() + nCount
;
240 auto pTo
= pFrom
+ 1;
244 ++((*pTo
)->m_nOffset
);
252 // If it does not fit, then insert a new block. But if there is more
253 // than 50% space in the array then compress first.
254 if( /*nBlock == nMaxBlock &&*/
255 m_nBlock
> ( m_nSize
/ ( MAXENTRY
/ 2 ) ) &&
258 // Something was moved before the current position and all
259 // pointer might be invalid. Thus restart Insert.
260 Insert( pElem
, pos
);
264 q
= InsBlock( cur
+1 );
267 // entry does not fit anymore - clear space
268 BigPtrEntry
* pLast
= p
->mvData
[ MAXENTRY
-1 ];
269 pLast
->m_nOffset
= 0;
272 q
->mvData
[ 0 ] = pLast
;
279 // now we have free space - insert
281 assert(pos
< MAXENTRY
);
282 if( pos
!= p
->nElem
)
284 int nCount
= p
->nElem
- sal_uInt16(pos
);
285 auto pFrom
= p
->mvData
.begin() + p
->nElem
;
286 auto pTo
= pFrom
+ 1;
290 ++( *pTo
)->m_nOffset
;
293 // insert element and update indices
294 pElem
->m_nOffset
= sal_uInt16(pos
);
296 p
->mvData
[ pos
] = pElem
;
300 if( cur
!= ( m_nBlock
- 1 ) ) UpdIndex( cur
);
303 CHECKIDX( m_ppInf
.get(), m_nBlock
, m_nSize
, m_nCur
);
306 void BigPtrArray::Remove( sal_Int32 pos
, sal_Int32 n
)
308 CHECKIDX( m_ppInf
.get(), m_nBlock
, m_nSize
, m_nCur
);
310 sal_uInt16 nBlkdel
= 0; // deleted blocks
311 sal_uInt16 cur
= Index2Block( pos
); // current block number
312 sal_uInt16 nBlk1
= cur
; // 1st treated block
313 sal_uInt16 nBlk1del
= USHRT_MAX
; // 1st deleted block
314 BlockInfo
* p
= m_ppInf
[ cur
];
320 sal_uInt16 nel
= p
->nElem
- sal_uInt16(pos
);
321 if( sal_Int32(nel
) > nElem
)
322 nel
= sal_uInt16(nElem
);
323 // move elements if needed
324 if( ( pos
+ nel
) < sal_Int32(p
->nElem
) )
326 auto pTo
= p
->mvData
.begin() + pos
;
327 auto pFrom
= pTo
+ nel
;
328 int nCount
= p
->nElem
- nel
- sal_uInt16(pos
);
332 (*pTo
)->m_nOffset
= (*pTo
)->m_nOffset
- nel
;
337 p
->nElem
= p
->nElem
- nel
;
338 // possibly delete block completely
342 if( USHRT_MAX
== nBlk1del
)
348 p
= m_ppInf
[ ++cur
];
352 // update table if blocks were removed
355 for( sal_uInt16 i
= nBlk1del
; i
< ( nBlk1del
+ nBlkdel
); i
++ )
358 if( ( nBlk1del
+ nBlkdel
) < m_nBlock
)
360 memmove( m_ppInf
.get() + nBlk1del
, m_ppInf
.get() + nBlk1del
+ nBlkdel
,
361 ( m_nBlock
- nBlkdel
- nBlk1del
) * sizeof( BlockInfo
* ) );
363 // UpdateIdx updates the successor thus start before first elem
368 p
->nEnd
= p
->nElem
-1;
375 BlockDel( nBlkdel
); // blocks were deleted
379 if( nBlk1
!= ( m_nBlock
- 1 ) && m_nSize
)
383 // call Compress() if there is more than 50% space in the array
384 if( m_nBlock
> ( m_nSize
/ ( MAXENTRY
/ 2 ) ) )
387 CHECKIDX( m_ppInf
.get(), m_nBlock
, m_nSize
, m_nCur
);
390 void BigPtrArray::Replace( sal_Int32 idx
, BigPtrEntry
* pElem
)
392 assert(idx
< m_nSize
); // Index out of bounds
393 m_nCur
= Index2Block( idx
);
394 BlockInfo
* p
= m_ppInf
[ m_nCur
];
395 pElem
->m_nOffset
= sal_uInt16(idx
- p
->nStart
);
397 p
->mvData
[ idx
- p
->nStart
] = pElem
;
400 /** Compress the array */
401 sal_uInt16
BigPtrArray::Compress()
403 CHECKIDX( m_ppInf
.get(), m_nBlock
, m_nSize
, m_nCur
);
405 // Iterate over InfoBlock array from beginning to end. If there is a deleted
406 // block in between so move all following ones accordingly. The pointer <pp>
407 // represents the "old" and <qq> the "new" array.
408 BlockInfo
** pp
= m_ppInf
.get(), **qq
= pp
;
410 BlockInfo
* pLast(nullptr); // last empty block
411 sal_uInt16 nLast
= 0; // missing elements
412 sal_uInt16 nBlkdel
= 0; // number of deleted blocks
413 sal_uInt16 nFirstChgPos
= USHRT_MAX
; // at which position was the 1st change?
415 // convert fill percentage into number of remaining elements
416 short const nMax
= MAXENTRY
- tools::Long(MAXENTRY
) * COMPRESSLVL
/ 100;
418 for( sal_uInt16 cur
= 0; cur
< m_nBlock
; ++cur
)
421 sal_uInt16 n
= p
->nElem
;
422 // Check if a not completely full block will be ignored. This happens if
423 // the current block would have to be split but the filling of the
424 // inspected block is already over its threshold value. In this case we
425 // do not fill more (it's expensive because of a double memmove() call)
426 if( nLast
&& ( n
> nLast
) && ( nLast
< nMax
) )
430 if( USHRT_MAX
== nFirstChgPos
)
433 // Not full yet? Then fill up.
437 // move elements from current to last block
438 auto pElem
= pLast
->mvData
.begin() + pLast
->nElem
;
439 auto pFrom
= p
->mvData
.begin();
440 for( sal_uInt16 nCount
= n
, nOff
= pLast
->nElem
;
441 nCount
; --nCount
, ++pElem
)
444 (*pElem
)->m_pBlock
= pLast
;
445 (*pElem
)->m_nOffset
= nOff
++;
449 pLast
->nElem
= pLast
->nElem
+ n
;
451 p
->nElem
= p
->nElem
- n
;
453 // Is the current block now empty as a result?
463 pElem
= p
->mvData
.begin();
465 int nCount
= p
->nElem
;
469 (*pElem
)->m_nOffset
= (*pElem
)->m_nOffset
- n
;
475 if( p
) // BlockInfo was not deleted
477 *qq
++ = p
; // adjust to correct position
479 // keep the potentially existing last half-full block
480 if( !nLast
&& p
->nElem
< MAXENTRY
)
483 nLast
= MAXENTRY
- p
->nElem
;
488 // if blocks were deleted shrink BlockInfo array if needed
494 p
->nEnd
= p
->nElem
- 1;
497 if( m_nCur
>= nFirstChgPos
)
500 CHECKIDX( m_ppInf
.get(), m_nBlock
, m_nSize
, m_nCur
);
505 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */