1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: bparr.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
40 // die Blockverwaltung waechst/schrumpft immer um 20 Bloecke, das sind dann
41 // immer ~ 20 * MAXENTRY == 20000 Eintraege
42 const USHORT nBlockGrowSize
= 20;
46 #define CHECKIDX( p, n, i, c )
50 #define CHECKIDX( p, n, i, c ) CheckIdx( p, n, i, c );
52 void CheckIdx( BlockInfo
** ppInf
, USHORT nBlock
, ULONG nSize
, USHORT nCur
)
54 DBG_ASSERT( !nSize
|| nCur
< nBlock
, "BigPtrArray: CurIndex steht falsch" );
57 for( USHORT nCnt
= 0; nCnt
< nBlock
; ++nCnt
, ++ppInf
)
59 nIdx
+= (*ppInf
)->nElem
;
60 // Array mit Luecken darf es nicht geben
61 DBG_ASSERT( !nCnt
|| (*(ppInf
-1))->nEnd
+ 1 == (*ppInf
)->nStart
,
62 "BigPtrArray: Luecke in der Verwaltung!" );
65 DBG_ASSERT( nIdx
== nSize
, "BigPtrArray: Anzahl ungueltig" );
71 BigPtrArray::BigPtrArray()
75 nMaxBlock
= nBlockGrowSize
; // == 20 * 1000 Eintraege
76 ppInf
= new BlockInfo
* [ nMaxBlock
];
81 BigPtrArray::~BigPtrArray()
85 BlockInfo
** pp
= ppInf
;
86 for( USHORT n
= 0; n
< nBlock
; ++n
, ++pp
)
88 delete[] (*pp
)->pData
;
95 // Auch der Move ist schlicht. Optimieren ist hier wg. der
96 // Stueckelung des Feldes zwecklos!
98 void BigPtrArray::Move( ULONG from
, ULONG to
)
100 USHORT cur
= Index2Block( from
);
101 BlockInfo
* p
= ppInf
[ cur
];
102 ElementPtr pElem
= p
->pData
[ from
- p
->nStart
];
103 Insert( pElem
, to
); // erst einfuegen, dann loeschen !!!!
104 Remove( ( to
< from
) ? ( from
+ 1 ) : from
);
107 // das Ende ist EXCLUSIV
110 void BigPtrArray::ForEach( ULONG nStart
, ULONG nEnd
,
111 FnForEach fn
, void* pArgs
)
118 USHORT cur
= Index2Block( nStart
);
119 BlockInfo
** pp
= ppInf
+ cur
;
121 USHORT nElem
= USHORT( nStart
- p
->nStart
);
122 ElementPtr
* pElem
= p
->pData
+ nElem
;
123 nElem
= p
->nElem
- nElem
;
126 if( !(*fn
)( *pElem
++, pArgs
) || ++nStart
>= nEnd
)
142 ElementPtr
BigPtrArray::operator[]( ULONG idx
) const
144 // weil die Funktion eben doch nicht const ist:
145 DBG_ASSERT( idx
< nSize
, "operator[]: Index aussserhalb" );
146 BigPtrArray
* pThis
= (BigPtrArray
*) this;
147 USHORT cur
= Index2Block( idx
);
148 BlockInfo
* p
= ppInf
[ cur
];
150 return p
->pData
[ idx
- p
->nStart
];
153 ///////////////////////////////////////////////////////////////////////////
157 // Suchen des Blocks einer bestimmten Position
159 // 1. Test, ob der letzte Block der gesuchte Block ist
160 // 2. Sonderfall: Index = 0?
161 // 3. Test der Nachbarbloecke
167 USHORT
BigPtrArray::Index2Block( ULONG pos
) const
169 // zuletzt verwendeter Block?
170 BlockInfo
* p
= ppInf
[ nCur
];
171 if( p
->nStart
<= pos
&& p
->nEnd
>= pos
)
177 if( nCur
< ( nBlock
- 1 ) )
180 if( p
->nStart
<= pos
&& p
->nEnd
>= pos
)
183 // vorangehender Block?
184 else if( pos
< p
->nStart
&& nCur
> 0 )
187 if( p
->nStart
<= pos
&& p
->nEnd
>= pos
)
191 // Diese fuehrt immer zum Erfolg
192 USHORT lower
= 0, upper
= nBlock
- 1;
196 USHORT n
= lower
+ ( upper
- lower
) / 2;
197 cur
= ( n
== cur
) ? n
+1 : n
;
199 if( p
->nStart
<= pos
&& p
->nEnd
>= pos
)
201 if( p
->nStart
> pos
)
209 // Update aller Indexbereiche ab einer bestimmten Position
211 // pos bezeichnet den letzten korrekten Block
213 void BigPtrArray::UpdIndex( USHORT pos
)
215 BlockInfo
** pp
= ppInf
+ pos
;
216 ULONG idx
= (*pp
)->nEnd
+ 1;
218 while( ++pos
< nBlock
)
227 // Einrichten eines neuen Blocks an einer bestimmten Position
229 // Vorhandene Blocks werden nach hinten verschoben
233 BlockInfo
* BigPtrArray::InsBlock( USHORT pos
)
235 if( nBlock
== nMaxBlock
)
237 // dann sollte wir mal das Array erweitern
238 BlockInfo
** ppNew
= new BlockInfo
* [ nMaxBlock
+ nBlockGrowSize
];
239 memcpy( ppNew
, ppInf
, nMaxBlock
* sizeof( BlockInfo
* ));
241 nMaxBlock
+= nBlockGrowSize
;
245 memmove( ppInf
+ pos
+1, ppInf
+ pos
,
246 ( nBlock
- pos
) * sizeof (BlockInfo
*) );
248 BlockInfo
* p
= new BlockInfo
;
252 p
->nStart
= p
->nEnd
= ppInf
[ pos
-1 ]->nEnd
+ 1;
254 p
->nStart
= p
->nEnd
= 0;
255 p
->nEnd
--; // keine Elemente
257 p
->pData
= new ElementPtr
[ MAXENTRY
];
262 void BigPtrArray::BlockDel( USHORT nDel
)
264 nBlock
= nBlock
- nDel
;
265 if( nMaxBlock
- nBlock
> nBlockGrowSize
)
267 // dann koennen wir wieder schrumpfen
268 nDel
= (( nBlock
/ nBlockGrowSize
) + 1 ) * nBlockGrowSize
;
269 BlockInfo
** ppNew
= new BlockInfo
* [ nDel
];
270 memcpy( ppNew
, ppInf
, nBlock
* sizeof( BlockInfo
* ));
278 void BigPtrArray::Insert( const ElementPtr
& rElem
, ULONG pos
)
280 CHECKIDX( ppInf
, nBlock
, nSize
, nCur
);
285 // Sonderfall: erstes Element einfuegen
286 p
= InsBlock( cur
= 0 );
287 else if( pos
== nSize
)
289 // Sonderfall: Einfuegen am Ende
292 if( p
->nElem
== MAXENTRY
)
293 // Der letzte Block ist voll, neuen anlegen
294 p
= InsBlock( ++cur
);
299 cur
= Index2Block( pos
);
302 if( p
->nElem
== MAXENTRY
)
304 // passt der letzte Eintrag in den naechsten Block?
306 if( cur
< ( nBlock
- 1 ) && ppInf
[ cur
+1 ]->nElem
< MAXENTRY
)
311 int nCount
= q
->nElem
;
312 ElementPtr
*pFrom
= q
->pData
+ nCount
,
315 ++( *--pTo
= *--pFrom
)->nOffset
;
322 // Wenn er auch nicht in den Folgeblock passt, muss ein
323 // neuer Block eingefuegt werden
324 // erst mal bei Bedarf komprimieren
326 // wenn mehr als 50% "Luft" im Array ist, dann sollte man mal das
328 if( /*nBlock == nMaxBlock &&*/
329 nBlock
> ( nSize
/ ( MAXENTRY
/ 2 ) ) &&
332 // es wurde vor der akt. Pos etwas verschoben und alle
333 // Pointer koennen ungueltig sein. Also das Insert
334 // nochmals aufsetzen
335 Insert( rElem
, pos
);
339 q
= InsBlock( cur
+1 );
342 // Eintrag passt nicht mehr. Dann muss Platz gemacht werden
343 ElementPtr pLast
= p
->pData
[ MAXENTRY
-1 ];
347 q
->pData
[ 0 ] = pLast
;
354 // Nun haben wir einen freien Block am Wickel: eintragen
356 DBG_ASSERT( pos
< MAXENTRY
, "falsche Pos" );
357 if( pos
!= p
->nElem
)
359 int nCount
= p
->nElem
- USHORT(pos
);
360 ElementPtr
*pFrom
= p
->pData
+ p
->nElem
,
363 ++( *--pTo
= *--pFrom
)->nOffset
;
365 // Element eintragen und Indexe updaten
366 ((ElementPtr
&)rElem
)->nOffset
= USHORT(pos
);
367 ((ElementPtr
&)rElem
)->pBlock
= p
;
368 p
->pData
[ pos
] = rElem
;
372 if( cur
!= ( nBlock
- 1 ) ) UpdIndex( cur
);
375 CHECKIDX( ppInf
, nBlock
, nSize
, nCur
);
378 void BigPtrArray::Remove( ULONG pos
, ULONG n
)
380 CHECKIDX( ppInf
, nBlock
, nSize
, nCur
);
382 USHORT nBlkdel
= 0; // entfernte Bloecke
383 USHORT cur
= Index2Block( pos
); // aktuelle Blocknr
384 USHORT nBlk1
= cur
; // 1. behandelter Block
385 USHORT nBlk1del
= USHRT_MAX
; // 1. entfernter Block
386 BlockInfo
* p
= ppInf
[ cur
];
391 USHORT nel
= p
->nElem
- USHORT(pos
);
392 if( ULONG(nel
) > nElem
)
394 // Eventuell Elemente verschieben
395 if( ( pos
+ nel
) < ULONG(p
->nElem
) )
397 ElementPtr
*pTo
= p
->pData
+ pos
,
399 int nCount
= p
->nElem
- nel
- USHORT(pos
);
403 (*pTo
)->nOffset
= (*pTo
)->nOffset
- nel
;
408 p
->nElem
= p
->nElem
- nel
;
411 // eventuell Block ganz entfernen
414 if( USHRT_MAX
== nBlk1del
)
423 // Am Ende die Tabelle updaten, falls Bloecke geloescht waren
426 // loeschen sollte man immer !!
427 for( USHORT i
= nBlk1del
; i
< ( nBlk1del
+ nBlkdel
); i
++ )
430 if( ( nBlk1del
+ nBlkdel
) < nBlock
)
432 memmove( ppInf
+ nBlk1del
, ppInf
+ nBlk1del
+ nBlkdel
,
433 ( nBlock
- nBlkdel
- nBlk1del
) * sizeof( BlockInfo
* ) );
435 // JP 19.07.95: nicht den ersten behandelten, sondern den davor!!
436 // UpdateIdx updatet nur alle Nachfolgende!!
441 p
->nEnd
= p
->nElem
-1;
446 BlockDel( nBlkdel
); // es wurden Bloecke geloescht
450 if( nBlk1
!= ( nBlock
- 1 ) && nSize
)
454 // wenn mehr als 50% "Luft" im Array ist, dann sollte man mal das
456 if( nBlock
> ( nSize
/ ( MAXENTRY
/ 2 ) ) )
459 CHECKIDX( ppInf
, nBlock
, nSize
, nCur
);
463 void BigPtrArray::Replace( ULONG idx
, const ElementPtr
& rElem
)
465 // weil die Funktion eben doch nicht const ist:
466 DBG_ASSERT( idx
< nSize
, "Set: Index aussserhalb" );
467 BigPtrArray
* pThis
= (BigPtrArray
*) this;
468 USHORT cur
= Index2Block( idx
);
469 BlockInfo
* p
= ppInf
[ cur
];
471 ((ElementPtr
&)rElem
)->nOffset
= USHORT(idx
- p
->nStart
);
472 ((ElementPtr
&)rElem
)->pBlock
= p
;
473 p
->pData
[ idx
- p
->nStart
] = rElem
;
477 // Array komprimieren
479 USHORT
BigPtrArray::Compress( short nMax
)
481 CHECKIDX( ppInf
, nBlock
, nSize
, nCur
);
483 // Es wird von vorne nach hinten ueber das InfoBlock Array iteriert.
484 // Wenn zwischen durch Block gel�scht werden, dann mussen alle
485 // nachfolgenden verschoben werden. Dazu werden die Pointer pp und qq
486 // benutzt; wobei pp das "alte" Array, qq das "neue" Array ist.
487 BlockInfo
** pp
= ppInf
, **qq
= pp
;
489 BlockInfo
* pLast(0); // letzter nicht voller Block
490 USHORT nLast
= 0; // fehlende Elemente
491 USHORT nBlkdel
= 0; // Anzahl der geloeschte Bloecke
492 USHORT nFirstChgPos
= USHRT_MAX
; // ab welcher Pos gab es die 1. Aenderung?
494 // von Fuell-Prozenten auf uebrige Eintrage umrechnen
495 nMax
= MAXENTRY
- (long) MAXENTRY
* nMax
/ 100;
497 for( USHORT cur
= 0; cur
< nBlock
; ++cur
)
501 // Testen, ob der noch nicht volle Block so gelassen wird
502 // dies ist der Fall, wenn der aktuelle Block gesplittet
503 // werden muesste, der noch nicht volle Block aber bereits
504 // ueber dem uebergebenen Break-Wert voll ist. In diesem
505 // Fall wird von einer weiteren Fuellung (die ja wegen dem
506 // zweifachen memmove() zeitaufwendig ist) abgesehen.
507 if( nLast
&& ( n
> nLast
) && ( nLast
< nMax
) )
511 if( USHRT_MAX
== nFirstChgPos
)
514 // ein nicht voller Block vorhanden: auffuellen
518 // Elemente uebertragen, vom akt. in den letzten
519 ElementPtr
* pElem
= pLast
->pData
+ pLast
->nElem
;
520 ElementPtr
* pFrom
= p
->pData
;
521 for( USHORT nCount
= n
, nOff
= pLast
->nElem
;
522 nCount
; --nCount
, ++pElem
)
524 (*pElem
)->pBlock
= pLast
,
525 (*pElem
)->nOffset
= nOff
++;
528 pLast
->nElem
= pLast
->nElem
+ n
;
530 p
->nElem
= p
->nElem
- n
;
532 // Ist der aktuelle Block dadurch leer geworden?
535 // dann kann der entfernt werden
542 pElem
= p
->pData
, pFrom
= pElem
+ n
;
543 int nCount
= p
->nElem
;
547 (*pElem
)->nOffset
= (*pElem
)->nOffset
- n
;
553 if( p
) // die Blockinfo wurde nicht geloescht
555 *qq
++ = p
; // dann setze sie an die richtige neue Position
557 // eventuell den letzten halbvollen Block festhalten
558 if( !nLast
&& p
->nElem
< MAXENTRY
)
561 nLast
= MAXENTRY
- p
->nElem
;
566 // Bloecke geloescht wurden, ggfs. das BlockInfo Array verkuerzen
570 // Und neu durchindizieren
572 p
->nEnd
= p
->nElem
- 1;
575 if( nCur
>= nFirstChgPos
)
578 CHECKIDX( ppInf
, nBlock
, nSize
, nCur
);