xref: /AOO41X/main/sw/source/core/bastyp/bparr.cxx (revision efeef26f81c84063fb0a91bde3856d4a51172d90)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 
28 
29 #include <string.h>
30 #include <limits.h>
31 #include "bparr.hxx"
32 
33 // die Blockverwaltung waechst/schrumpft immer um 20 Bloecke, das sind dann
34 // immer ~ 20 * MAXENTRY == 20000 Eintraege
35 const sal_uInt16 nBlockGrowSize = 20;
36 
37 #ifndef DBG_UTIL
38 
39 #define CHECKIDX( p, n, i, c )
40 
41 #else
42 
43 #define CHECKIDX( p, n, i, c ) CheckIdx( p, n, i, c );
44 
CheckIdx(BlockInfo ** ppInf,sal_uInt16 nBlock,sal_uLong nSize,sal_uInt16 nCur)45 void CheckIdx( BlockInfo** ppInf, sal_uInt16 nBlock, sal_uLong nSize, sal_uInt16 nCur )
46 {
47     DBG_ASSERT( !nSize || nCur < nBlock, "BigPtrArray: CurIndex steht falsch" );
48 
49     sal_uLong nIdx = 0;
50     for( sal_uInt16 nCnt = 0; nCnt < nBlock; ++nCnt, ++ppInf )
51     {
52         nIdx += (*ppInf)->nElem;
53         // Array mit Luecken darf es nicht geben
54         DBG_ASSERT( !nCnt || (*(ppInf-1))->nEnd + 1 == (*ppInf)->nStart,
55                     "BigPtrArray: Luecke in der Verwaltung!" );
56     }
57 
58     DBG_ASSERT( nIdx == nSize, "BigPtrArray: Anzahl ungueltig" );
59 }
60 
61 #endif
62 
63 
BigPtrArray()64 BigPtrArray::BigPtrArray()
65 {
66     nBlock = nCur = 0;
67     nSize = 0;
68     nMaxBlock = nBlockGrowSize;     // == 20 * 1000 Eintraege
69     ppInf = new BlockInfo* [ nMaxBlock ];
70 }
71 
72 
73 
~BigPtrArray()74 BigPtrArray::~BigPtrArray()
75 {
76     if( nBlock )
77     {
78         BlockInfo** pp = ppInf;
79         for( sal_uInt16 n = 0; n < nBlock; ++n, ++pp )
80         {
81             delete[] (*pp)->pData;
82             delete    *pp;
83         }
84     }
85     delete[] ppInf;
86 }
87 
88 // Auch der Move ist schlicht. Optimieren ist hier wg. der
89 // Stueckelung des Feldes zwecklos!
90 
Move(sal_uLong from,sal_uLong to)91 void BigPtrArray::Move( sal_uLong from, sal_uLong to )
92 {
93     sal_uInt16 cur = Index2Block( from );
94     BlockInfo* p = ppInf[ cur ];
95     ElementPtr pElem = p->pData[ from - p->nStart ];
96     Insert( pElem, to );            // erst einfuegen, dann loeschen !!!!
97     Remove( ( to < from ) ? ( from + 1 ) : from );
98 }
99 
100 // das Ende ist EXCLUSIV
101 
102 
ForEach(sal_uLong nStart,sal_uLong nEnd,FnForEach fn,void * pArgs)103 void BigPtrArray::ForEach( sal_uLong nStart, sal_uLong nEnd,
104                             FnForEach fn, void* pArgs )
105 {
106     if( nEnd > nSize )
107         nEnd = nSize;
108 
109     if( nStart < nEnd )
110     {
111         sal_uInt16 cur = Index2Block( nStart );
112         BlockInfo** pp = ppInf + cur;
113         BlockInfo* p = *pp;
114         sal_uInt16 nElem = sal_uInt16( nStart - p->nStart );
115         ElementPtr* pElem = p->pData + nElem;
116         nElem = p->nElem - nElem;
117         for(;;)
118         {
119             if( !(*fn)( *pElem++, pArgs ) || ++nStart >= nEnd )
120                 break;
121 
122             // naechstes Element
123             if( !--nElem )
124             {
125                 // neuer Block
126                 p = *++pp;
127                 pElem = p->pData;
128                 nElem = p->nElem;
129             }
130         }
131     }
132 }
133 
134 
operator [](sal_uLong idx) const135 ElementPtr BigPtrArray::operator[]( sal_uLong idx ) const
136 {
137     // weil die Funktion eben doch nicht const ist:
138     DBG_ASSERT( idx < nSize, "operator[]: Index aussserhalb" );
139     BigPtrArray* pThis = (BigPtrArray*) this;
140     sal_uInt16 cur = Index2Block( idx );
141     BlockInfo* p = ppInf[ cur ];
142     pThis->nCur = cur;
143     return p->pData[ idx - p->nStart ];
144 }
145 
146 ///////////////////////////////////////////////////////////////////////////
147 
148 // private Methoden
149 
150 // Suchen des Blocks einer bestimmten Position
151 // Algorithmus:
152 // 1. Test, ob der letzte Block der gesuchte Block ist
153 // 2. Sonderfall: Index = 0?
154 // 3. Test der Nachbarbloecke
155 
156 // 4. Binaere Suche
157 
158 
159 
Index2Block(sal_uLong pos) const160 sal_uInt16 BigPtrArray::Index2Block( sal_uLong pos ) const
161 {
162     // zuletzt verwendeter Block?
163     BlockInfo* p = ppInf[ nCur ];
164     if( p->nStart <= pos && p->nEnd >= pos )
165         return nCur;
166     // Index = 0?
167     if( !pos )
168         return 0;
169     // Folgeblock?
170     if( nCur < ( nBlock - 1 ) )
171     {
172         p = ppInf[ nCur+1 ];
173         if( p->nStart <= pos && p->nEnd >= pos )
174             return nCur+1;
175     }
176     // vorangehender Block?
177     else if( pos < p->nStart && nCur > 0 )
178     {
179         p = ppInf[ nCur-1 ];
180         if( p->nStart <= pos && p->nEnd >= pos )
181             return nCur-1;
182     }
183     // Binaere Suche:
184     // Diese fuehrt immer zum Erfolg
185     sal_uInt16 lower = 0, upper = nBlock - 1;
186     sal_uInt16 cur = 0;
187     for(;;)
188     {
189         sal_uInt16 n = lower + ( upper - lower ) / 2;
190         cur = ( n == cur ) ? n+1 : n;
191         p = ppInf[ cur ];
192         if( p->nStart <= pos && p->nEnd >= pos )
193             return cur;
194         if( p->nStart > pos )
195             upper = cur;
196         else
197             lower = cur;
198     }
199 }
200 
201 
202 // Update aller Indexbereiche ab einer bestimmten Position
203 
204 // pos bezeichnet den letzten korrekten Block
205 
UpdIndex(sal_uInt16 pos)206 void BigPtrArray::UpdIndex( sal_uInt16 pos )
207 {
208     BlockInfo** pp = ppInf + pos;
209     sal_uLong idx = (*pp)->nEnd + 1;
210     BlockInfo* p;
211     while( ++pos < nBlock )
212     {
213         p = *++pp;
214         p->nStart = idx;
215         idx       += p->nElem;
216         p->nEnd   = idx - 1;
217     }
218 }
219 
220 // Einrichten eines neuen Blocks an einer bestimmten Position
221 
222 // Vorhandene Blocks werden nach hinten verschoben
223 
224 
225 
InsBlock(sal_uInt16 pos)226 BlockInfo* BigPtrArray::InsBlock( sal_uInt16 pos )
227 {
228     if( nBlock == nMaxBlock )
229     {
230         // dann sollte wir mal das Array erweitern
231         BlockInfo** ppNew = new BlockInfo* [ nMaxBlock + nBlockGrowSize ];
232         memcpy( ppNew, ppInf, nMaxBlock * sizeof( BlockInfo* ));
233         delete[] ppInf;
234         nMaxBlock += nBlockGrowSize;
235         ppInf = ppNew;
236     }
237     if( pos != nBlock )
238         memmove( ppInf + pos+1, ppInf + pos ,
239                  ( nBlock - pos ) * sizeof (BlockInfo*) );
240     ++nBlock;
241     BlockInfo* p = new BlockInfo;
242     ppInf[ pos ] = p;
243 
244     if( pos )
245         p->nStart = p->nEnd = ppInf[ pos-1 ]->nEnd + 1;
246     else
247         p->nStart = p->nEnd = 0;
248     p->nEnd--;  // keine Elemente
249     p->nElem = 0;
250     p->pData = new ElementPtr [ MAXENTRY ];
251     p->pBigArr = this;
252     return p;
253 }
254 
BlockDel(sal_uInt16 nDel)255 void BigPtrArray::BlockDel( sal_uInt16 nDel )
256 {
257     nBlock = nBlock - nDel;
258     if( nMaxBlock - nBlock > nBlockGrowSize )
259     {
260         // dann koennen wir wieder schrumpfen
261         nDel = (( nBlock / nBlockGrowSize ) + 1 ) * nBlockGrowSize;
262         BlockInfo** ppNew = new BlockInfo* [ nDel ];
263         memcpy( ppNew, ppInf, nBlock * sizeof( BlockInfo* ));
264         delete[] ppInf;
265         ppInf = ppNew;
266         nMaxBlock = nDel;
267     }
268 }
269 
270 
Insert(const ElementPtr & rElem,sal_uLong pos)271 void BigPtrArray::Insert( const ElementPtr& rElem, sal_uLong pos )
272 {
273     CHECKIDX( ppInf, nBlock, nSize, nCur );
274 
275     BlockInfo* p;
276     sal_uInt16 cur;
277     if( !nSize )
278         // Sonderfall: erstes Element einfuegen
279         p = InsBlock( cur = 0 );
280     else if( pos == nSize )
281     {
282         // Sonderfall: Einfuegen am Ende
283         cur = nBlock - 1;
284         p = ppInf[ cur ];
285         if( p->nElem == MAXENTRY )
286             // Der letzte Block ist voll, neuen anlegen
287             p = InsBlock( ++cur );
288     }
289     else
290     {
291         // Standardfall:
292         cur = Index2Block( pos );
293         p = ppInf[ cur ];
294     }
295     if( p->nElem == MAXENTRY )
296     {
297         // passt der letzte Eintrag in den naechsten Block?
298         BlockInfo* q;
299         if( cur < ( nBlock - 1 ) && ppInf[ cur+1 ]->nElem < MAXENTRY )
300         {
301             q = ppInf[ cur+1 ];
302             if( q->nElem )
303             {
304                 int nCount = q->nElem;
305                 ElementPtr *pFrom = q->pData + nCount,
306                                     *pTo = pFrom+1;
307                 while( nCount-- )
308                     ++( *--pTo = *--pFrom )->nOffset;
309             }
310             q->nStart--;
311             q->nEnd--;
312         }
313         else
314         {
315             // Wenn er auch nicht in den Folgeblock passt, muss ein
316             // neuer Block eingefuegt werden
317             // erst mal bei Bedarf komprimieren
318 
319             // wenn mehr als 50% "Luft" im Array ist, dann sollte man mal das
320             // Compress aufrufen
321             if( /*nBlock == nMaxBlock &&*/
322                 nBlock > ( nSize / ( MAXENTRY / 2 ) ) &&
323                 cur >= Compress() )
324             {
325                 // es wurde vor der akt. Pos etwas verschoben und alle
326                 // Pointer koennen ungueltig sein. Also das Insert
327                 // nochmals aufsetzen
328                 Insert( rElem, pos );
329                 return ;
330             }
331 
332             q = InsBlock( cur+1 );
333         }
334 
335         // Eintrag passt nicht mehr. Dann muss Platz gemacht werden
336         ElementPtr pLast = p->pData[ MAXENTRY-1 ];
337         pLast->nOffset = 0;
338         pLast->pBlock = q;
339 
340         q->pData[ 0 ] = pLast;
341         q->nElem++;
342         q->nEnd++;
343 
344         p->nEnd--;
345         p->nElem--;
346     }
347     // Nun haben wir einen freien Block am Wickel: eintragen
348     pos -= p->nStart;
349     DBG_ASSERT( pos < MAXENTRY, "falsche Pos" );
350     if( pos != p->nElem )
351     {
352         int nCount = p->nElem - sal_uInt16(pos);
353         ElementPtr *pFrom = p->pData + p->nElem,
354                             *pTo = pFrom + 1;
355         while( nCount-- )
356             ++( *--pTo = *--pFrom )->nOffset;
357     }
358     // Element eintragen und Indexe updaten
359     ((ElementPtr&)rElem)->nOffset = sal_uInt16(pos);
360     ((ElementPtr&)rElem)->pBlock = p;
361     p->pData[ pos ] = rElem;
362     p->nEnd++;
363     p->nElem++;
364     nSize++;
365     if( cur != ( nBlock - 1 ) ) UpdIndex( cur );
366     nCur = cur;
367 
368     CHECKIDX( ppInf, nBlock, nSize, nCur );
369 }
370 
Remove(sal_uLong pos,sal_uLong n)371 void BigPtrArray::Remove( sal_uLong pos, sal_uLong n )
372 {
373     CHECKIDX( ppInf, nBlock, nSize, nCur );
374 
375     sal_uInt16 nBlkdel = 0;                 // entfernte Bloecke
376     sal_uInt16 cur = Index2Block( pos );    // aktuelle Blocknr
377     sal_uInt16 nBlk1 = cur;                 // 1. behandelter Block
378     sal_uInt16 nBlk1del = USHRT_MAX;        // 1. entfernter Block
379     BlockInfo* p = ppInf[ cur ];
380     pos -= p->nStart;
381     sal_uLong nElem = n;
382     while( nElem )
383     {
384         sal_uInt16 nel = p->nElem - sal_uInt16(pos);
385         if( sal_uLong(nel) > nElem )
386             nel = sal_uInt16(nElem);
387         // Eventuell Elemente verschieben
388         if( ( pos + nel ) < sal_uLong(p->nElem) )
389         {
390             ElementPtr *pTo = p->pData + pos,
391                                 *pFrom = pTo + nel;
392             int nCount = p->nElem - nel - sal_uInt16(pos);
393             while( nCount-- )
394             {
395                 *pTo = *pFrom++;
396                 (*pTo)->nOffset = (*pTo)->nOffset - nel;
397                 ++pTo;
398             }
399         }
400         p->nEnd -= nel;
401         p->nElem = p->nElem - nel;
402         if( !p->nElem )
403         {
404             // eventuell Block ganz entfernen
405             delete[] p->pData;
406             nBlkdel++;
407             if( USHRT_MAX == nBlk1del )
408                 nBlk1del = cur;
409         }
410         nElem -= nel;
411         if( !nElem )
412             break;
413         p = ppInf[ ++cur ];
414         pos = 0;
415     }
416     // Am Ende die Tabelle updaten, falls Bloecke geloescht waren
417     if( nBlkdel )
418     {
419         // loeschen sollte man immer !!
420         for( sal_uInt16 i = nBlk1del; i < ( nBlk1del + nBlkdel ); i++ )
421             delete ppInf[ i ];
422 
423         if( ( nBlk1del + nBlkdel ) < nBlock )
424         {
425             memmove( ppInf + nBlk1del, ppInf + nBlk1del + nBlkdel,
426                      ( nBlock - nBlkdel - nBlk1del ) * sizeof( BlockInfo* ) );
427 
428             // JP 19.07.95: nicht den ersten behandelten, sondern den davor!!
429             //              UpdateIdx updatet nur alle Nachfolgende!!
430             if( !nBlk1 )
431             {
432                 p = ppInf[ 0 ];
433                 p->nStart = 0;
434                 p->nEnd = p->nElem-1;
435             }
436             else
437                 --nBlk1;
438         }
439         BlockDel( nBlkdel );            // es wurden Bloecke geloescht
440     }
441 
442     nSize -= n;
443     if( nBlk1 != ( nBlock - 1 ) && nSize )
444         UpdIndex( nBlk1 );
445     nCur = nBlk1;
446 
447     // wenn mehr als 50% "Luft" im Array ist, dann sollte man mal das
448     // Compress aufrufen
449     if( nBlock > ( nSize / ( MAXENTRY / 2 ) ) )
450         Compress();
451 
452     CHECKIDX( ppInf, nBlock, nSize, nCur );
453 }
454 
455 
Replace(sal_uLong idx,const ElementPtr & rElem)456 void BigPtrArray::Replace( sal_uLong idx, const ElementPtr& rElem)
457 {
458     // weil die Funktion eben doch nicht const ist:
459     DBG_ASSERT( idx < nSize, "Set: Index aussserhalb" );
460     BigPtrArray* pThis = (BigPtrArray*) this;
461     sal_uInt16 cur = Index2Block( idx );
462     BlockInfo* p = ppInf[ cur ];
463     pThis->nCur = cur;
464     ((ElementPtr&)rElem)->nOffset = sal_uInt16(idx - p->nStart);
465     ((ElementPtr&)rElem)->pBlock = p;
466     p->pData[ idx - p->nStart ] = rElem;
467 }
468 
469 
470 // Array komprimieren
471 
Compress(short nMax)472 sal_uInt16 BigPtrArray::Compress( short nMax )
473 {
474     CHECKIDX( ppInf, nBlock, nSize, nCur );
475 
476     // Es wird von vorne nach hinten ueber das InfoBlock Array iteriert.
477     // Wenn zwischen durch Block gel�scht werden, dann mussen alle
478     // nachfolgenden verschoben werden. Dazu werden die Pointer pp und qq
479     // benutzt; wobei pp das "alte" Array, qq das "neue" Array ist.
480     BlockInfo** pp = ppInf, **qq = pp;
481     BlockInfo* p;
482     BlockInfo* pLast(0);                // letzter nicht voller Block
483     sal_uInt16 nLast = 0;                   // fehlende Elemente
484     sal_uInt16 nBlkdel = 0;                 // Anzahl der geloeschte Bloecke
485     sal_uInt16 nFirstChgPos = USHRT_MAX;    // ab welcher Pos gab es die 1. Aenderung?
486 
487     // von Fuell-Prozenten auf uebrige Eintrage umrechnen
488     nMax = MAXENTRY - (long) MAXENTRY * nMax / 100;
489 
490     for( sal_uInt16 cur = 0; cur < nBlock; ++cur )
491     {
492         p = *pp++;
493         sal_uInt16 n = p->nElem;
494         // Testen, ob der noch nicht volle Block so gelassen wird
495         // dies ist der Fall, wenn der aktuelle Block gesplittet
496         // werden muesste, der noch nicht volle Block aber bereits
497         // ueber dem uebergebenen Break-Wert voll ist. In diesem
498         // Fall wird von einer weiteren Fuellung (die ja wegen dem
499         // zweifachen memmove() zeitaufwendig ist) abgesehen.
500         if( nLast && ( n > nLast ) && ( nLast < nMax ) )
501             nLast = 0;
502         if( nLast )
503         {
504             if( USHRT_MAX == nFirstChgPos )
505                 nFirstChgPos = cur;
506 
507             // ein nicht voller Block vorhanden: auffuellen
508             if( n > nLast )
509                 n = nLast;
510 
511             // Elemente uebertragen, vom akt. in den letzten
512             ElementPtr* pElem = pLast->pData + pLast->nElem;
513             ElementPtr* pFrom = p->pData;
514             for( sal_uInt16 nCount = n, nOff = pLast->nElem;
515                             nCount; --nCount, ++pElem )
516                 *pElem = *pFrom++,
517                     (*pElem)->pBlock = pLast,
518                     (*pElem)->nOffset = nOff++;
519 
520             // korrigieren
521             pLast->nElem = pLast->nElem + n;
522             nLast = nLast - n;
523             p->nElem = p->nElem - n;
524 
525             // Ist der aktuelle Block dadurch leer geworden?
526             if( !p->nElem )
527             {
528                 // dann kann der entfernt werden
529                 delete[] p->pData;
530                 delete   p, p = 0;
531                 ++nBlkdel;
532             }
533             else
534             {
535                 pElem = p->pData, pFrom = pElem + n;
536                 int nCount = p->nElem;
537                 while( nCount-- )
538                 {
539                     *pElem = *pFrom++;
540                     (*pElem)->nOffset = (*pElem)->nOffset - n;
541                     ++pElem;
542                 }
543             }
544         }
545 
546         if( p )     // die Blockinfo wurde nicht geloescht
547         {
548             *qq++ = p;      // dann setze sie an die richtige neue Position
549 
550             // eventuell den letzten halbvollen Block festhalten
551             if( !nLast && p->nElem < MAXENTRY )
552             {
553                 pLast = p;
554                 nLast = MAXENTRY - p->nElem;
555             }
556         }
557     }
558 
559     // Bloecke geloescht wurden, ggfs. das BlockInfo Array verkuerzen
560     if( nBlkdel )
561         BlockDel( nBlkdel );
562 
563     // Und neu durchindizieren
564     p = ppInf[ 0 ];
565     p->nEnd = p->nElem - 1;
566     UpdIndex( 0 );
567 
568     if( nCur >= nFirstChgPos )
569         nCur = 0;
570 
571     CHECKIDX( ppInf, nBlock, nSize, nCur );
572 
573     return nFirstChgPos;
574 }
575 
576 
577