/src/libreoffice/sot/source/sdstor/stgavl.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <osl/diagnose.h> |
21 | | #include "stgavl.hxx" |
22 | | #include <assert.h> |
23 | | |
24 | | StgAvlNode::StgAvlNode() |
25 | 3.66M | { |
26 | 3.66M | m_pLeft = m_pRight = nullptr; |
27 | 3.66M | m_nBalance = m_nId = 0; |
28 | 3.66M | } |
29 | | |
30 | | StgAvlNode::~StgAvlNode() |
31 | 3.66M | { |
32 | 3.66M | delete m_pLeft; |
33 | 3.66M | delete m_pRight; |
34 | 3.66M | } |
35 | | |
36 | | StgAvlNode* StgAvlNode::Find( StgAvlNode const * pFind ) |
37 | 1.88M | { |
38 | 1.88M | if ( pFind ) |
39 | 1.88M | { |
40 | 1.88M | StgAvlNode* p = this; |
41 | 6.08M | while( p ) |
42 | 5.44M | { |
43 | 5.44M | sal_Int32 nRes = p->Compare( pFind ); |
44 | 5.44M | if( !nRes ) |
45 | 1.24M | return p; |
46 | 4.20M | else p = ( nRes < 0 ) ? p->m_pLeft : p->m_pRight; |
47 | 5.44M | } |
48 | 1.88M | } |
49 | 639k | return nullptr; |
50 | 1.88M | } |
51 | | |
52 | | // find point to add node to AVL tree and returns |
53 | | // +/0/- for >/=/< previous |
54 | | |
55 | | sal_Int32 StgAvlNode::Locate |
56 | | ( StgAvlNode const * pFind, |
57 | | StgAvlNode** pPivot, StgAvlNode **pParent, StgAvlNode** pPrev ) |
58 | 1.22M | { |
59 | 1.22M | sal_Int32 nRes = 0; |
60 | 1.22M | StgAvlNode* pCur = this; |
61 | | |
62 | 1.22M | assert(pPivot && pParent && pPrev && "The pointers may not be NULL!"); |
63 | 1.22M | *pParent = *pPrev = nullptr; |
64 | 1.22M | *pPivot = this; |
65 | | |
66 | | // search tree for insertion point |
67 | 1.22M | if ( pFind ) |
68 | 1.22M | { |
69 | 5.00M | while( pCur != nullptr ) |
70 | 3.84M | { |
71 | | // check for pPivot |
72 | 3.84M | if( pCur->m_nBalance != 0 ) |
73 | 2.85M | { |
74 | 2.85M | *pPivot = pCur; |
75 | 2.85M | *pParent = *pPrev; |
76 | 2.85M | } |
77 | | // save pPrev location and see what direction to go |
78 | 3.84M | *pPrev = pCur; |
79 | 3.84M | nRes = pCur->Compare( pFind ); |
80 | 3.84M | if( nRes == 0 ) |
81 | 57.2k | break; |
82 | 3.78M | else pCur = ( nRes < 0 ) ? pCur->m_pLeft : pCur->m_pRight; |
83 | 3.84M | } |
84 | 1.22M | } |
85 | | |
86 | 1.22M | return nRes; |
87 | 1.22M | } |
88 | | |
89 | | // adjust balance factors in AVL tree from pivot down. |
90 | | // Returns delta balance. |
91 | | |
92 | | short StgAvlNode::Adjust( StgAvlNode** pHeavy, StgAvlNode const * pNew ) |
93 | 1.16M | { |
94 | 1.16M | StgAvlNode* pCur = this; |
95 | 1.16M | short nDelta; |
96 | | // no traversing |
97 | 1.16M | if( pCur == pNew || !pNew ) |
98 | 0 | return m_nBalance; |
99 | | |
100 | 1.16M | assert(pHeavy && pNew && "The pointers is not allowed to be NULL!"); |
101 | | |
102 | 1.16M | sal_Int32 nRes = Compare( pNew ); |
103 | 1.16M | if( nRes > 0 ) |
104 | 882k | { |
105 | 882k | *pHeavy = pCur = m_pRight; |
106 | 882k | nDelta = -1; |
107 | 882k | } |
108 | 284k | else |
109 | 284k | { |
110 | 284k | *pHeavy = pCur = m_pLeft; |
111 | 284k | nDelta = 1; |
112 | 284k | } |
113 | 1.16M | m_nBalance = 0; |
114 | 1.89M | while( pCur != pNew ) |
115 | 729k | { |
116 | 729k | nRes = pCur->Compare( pNew ); |
117 | 729k | if( nRes > 0 ) |
118 | 544k | { |
119 | | // height of right increases by 1 |
120 | 544k | pCur->m_nBalance = -1; |
121 | 544k | pCur = pCur->m_pRight; |
122 | 544k | } |
123 | 184k | else |
124 | 184k | { |
125 | | // height of left increases by 1 |
126 | 184k | pCur->m_nBalance = 1; |
127 | 184k | pCur = pCur->m_pLeft; |
128 | 184k | } |
129 | 729k | } |
130 | 1.16M | m_nBalance = m_nBalance + nDelta; |
131 | 1.16M | return nDelta; |
132 | 1.16M | } |
133 | | |
134 | | // perform LL rotation and return new root |
135 | | |
136 | | StgAvlNode* StgAvlNode::RotLL() |
137 | 0 | { |
138 | 0 | assert(m_pLeft && "The pointer is not allowed to be NULL!"); |
139 | 0 | StgAvlNode *pHeavy = m_pLeft; |
140 | 0 | m_pLeft = pHeavy->m_pRight; |
141 | 0 | pHeavy->m_pRight = this; |
142 | 0 | pHeavy->m_nBalance = m_nBalance = 0; |
143 | 0 | return pHeavy; |
144 | 0 | } |
145 | | |
146 | | // perform LR rotation and return new root |
147 | | |
148 | | StgAvlNode* StgAvlNode::RotLR() |
149 | 0 | { |
150 | 0 | assert(m_pLeft && m_pLeft->m_pRight && "The pointer is not allowed to be NULL!"); |
151 | 0 | StgAvlNode* pHeavy = m_pLeft; |
152 | 0 | StgAvlNode* pNewRoot = pHeavy->m_pRight; |
153 | |
|
154 | 0 | pHeavy->m_pRight = pNewRoot->m_pLeft; |
155 | 0 | m_pLeft = pNewRoot->m_pRight; |
156 | 0 | pNewRoot->m_pLeft = pHeavy; |
157 | 0 | pNewRoot->m_pRight = this; |
158 | |
|
159 | 0 | switch( pNewRoot->m_nBalance ) |
160 | 0 | { |
161 | 0 | case 1: // LR( b ) |
162 | 0 | m_nBalance = -1; |
163 | 0 | pHeavy->m_nBalance = 0; |
164 | 0 | break; |
165 | 0 | case -1: // LR( c ) |
166 | 0 | pHeavy->m_nBalance = 1; |
167 | 0 | m_nBalance = 0; |
168 | 0 | break; |
169 | 0 | case 0: // LR( a ) |
170 | 0 | m_nBalance = 0; |
171 | 0 | pHeavy->m_nBalance = 0; |
172 | 0 | break; |
173 | 0 | } |
174 | 0 | pNewRoot->m_nBalance = 0; |
175 | 0 | return pNewRoot; |
176 | 0 | } |
177 | | |
178 | | // perform RR rotation and return new root |
179 | | StgAvlNode* StgAvlNode::RotRR() |
180 | 0 | { |
181 | 0 | assert(m_pRight && "The pointer is not allowed to be NULL!" ); |
182 | 0 | StgAvlNode* pHeavy = m_pRight; |
183 | 0 | m_pRight = pHeavy->m_pLeft; |
184 | 0 | pHeavy->m_pLeft = this; |
185 | 0 | m_nBalance = pHeavy->m_nBalance = 0; |
186 | 0 | return pHeavy; |
187 | 0 | } |
188 | | |
189 | | // perform the RL rotation and return the new root |
190 | | StgAvlNode* StgAvlNode::RotRL() |
191 | 0 | { |
192 | 0 | assert(m_pRight && m_pRight->m_pLeft && "The pointer is not allowed to be NULL!"); |
193 | 0 | StgAvlNode* pHeavy = m_pRight; |
194 | 0 | StgAvlNode* pNewRoot = pHeavy->m_pLeft; |
195 | 0 | pHeavy->m_pLeft = pNewRoot->m_pRight; |
196 | 0 | m_pRight = pNewRoot->m_pLeft; |
197 | 0 | pNewRoot->m_pRight = pHeavy; |
198 | 0 | pNewRoot->m_pLeft = this; |
199 | 0 | switch( pNewRoot->m_nBalance ) |
200 | 0 | { |
201 | 0 | case -1: // RL( b ) |
202 | 0 | m_nBalance = 1; |
203 | 0 | pHeavy->m_nBalance = 0; |
204 | 0 | break; |
205 | 0 | case 1: // RL( c ) |
206 | 0 | pHeavy->m_nBalance = -1; |
207 | 0 | m_nBalance = 0; |
208 | 0 | break; |
209 | 0 | case 0: // RL( a ) |
210 | 0 | m_nBalance = 0; |
211 | 0 | pHeavy->m_nBalance = 0; |
212 | 0 | break; |
213 | 0 | } |
214 | 0 | pNewRoot->m_nBalance = 0; |
215 | 0 | return pNewRoot; |
216 | 0 | } |
217 | | |
218 | | // Remove a tree element. Return the removed element or NULL. |
219 | | |
220 | | StgAvlNode* StgAvlNode::Rem( StgAvlNode** p, StgAvlNode* pDel, bool bPtrs ) |
221 | 4.50k | { |
222 | 4.50k | if( p && *p && pDel ) |
223 | 4.50k | { |
224 | 4.50k | StgAvlNode* pCur = *p; |
225 | 4.50k | sal_Int32 nRes = bPtrs ? sal_Int32( pCur == pDel ) : pCur->Compare( pDel ); |
226 | 4.50k | if( !nRes ) |
227 | 3.46k | { |
228 | | // Element found: remove |
229 | 3.46k | if( !pCur->m_pRight ) |
230 | 2.25k | { |
231 | 2.25k | *p = pCur->m_pLeft; pCur->m_pLeft = nullptr; |
232 | 2.25k | } |
233 | 1.21k | else if( !pCur->m_pLeft ) |
234 | 300 | { |
235 | 300 | *p = pCur->m_pRight; pCur->m_pRight = nullptr; |
236 | 300 | } |
237 | 916 | else |
238 | 916 | { |
239 | | // The damn element has two leaves. Get the |
240 | | // rightmost element of the left subtree (which |
241 | | // is lexically before this element) and replace |
242 | | // this element with the element found. |
243 | 916 | StgAvlNode* last = pCur; |
244 | 916 | StgAvlNode* l; |
245 | 916 | for( l = pCur->m_pLeft; |
246 | 3.75k | l->m_pRight; last = l, l = l->m_pRight ) {} |
247 | | // remove the element from chain |
248 | 916 | if( l == last->m_pRight ) |
249 | 826 | last->m_pRight = l->m_pLeft; |
250 | 90 | else |
251 | 90 | last->m_pLeft = l->m_pLeft; |
252 | | // perform the replacement |
253 | 916 | l->m_pLeft = pCur->m_pLeft; |
254 | 916 | l->m_pRight = pCur->m_pRight; |
255 | 916 | *p = l; |
256 | | // delete the element |
257 | 916 | pCur->m_pLeft = pCur->m_pRight = nullptr; |
258 | 916 | } |
259 | 3.46k | return pCur; |
260 | 3.46k | } |
261 | 1.03k | else |
262 | 1.03k | { |
263 | 1.03k | if( nRes < 0 ) |
264 | 12 | return Rem( &pCur->m_pLeft, pDel, bPtrs ); |
265 | 1.02k | else |
266 | 1.02k | return Rem( &pCur->m_pRight, pDel, bPtrs ); |
267 | 1.03k | } |
268 | 4.50k | } |
269 | 0 | return nullptr; |
270 | 4.50k | } |
271 | | |
272 | | // Enumerate the tree for later iteration |
273 | | |
274 | | void StgAvlNode::StgEnum( short& n ) |
275 | 5.06M | { |
276 | 5.06M | if( m_pLeft ) |
277 | 776k | m_pLeft->StgEnum( n ); |
278 | 5.06M | m_nId = n++; |
279 | 5.06M | if( m_pRight ) |
280 | 3.17M | m_pRight->StgEnum( n ); |
281 | 5.06M | } |
282 | | |
283 | | // Add node to AVL tree. |
284 | | // Return false if the element already exists. |
285 | | |
286 | | bool StgAvlNode::Insert( StgAvlNode** pRoot, StgAvlNode* pIns ) |
287 | 1.55M | { |
288 | 1.55M | StgAvlNode* pPivot, *pHeavy, *pParent, *pPrev; |
289 | 1.55M | if ( !pRoot ) |
290 | 0 | return false; |
291 | | |
292 | | // special case - empty tree |
293 | 1.55M | if( *pRoot == nullptr ) |
294 | 333k | { |
295 | 333k | *pRoot = pIns; |
296 | 333k | return true; |
297 | 333k | } |
298 | | // find insertion point and return if already present |
299 | 1.22M | sal_Int32 nRes = (*pRoot)->Locate( pIns, &pPivot, &pParent, &pPrev ); |
300 | 1.22M | if( !nRes ) |
301 | 57.2k | return false; |
302 | | |
303 | 1.22M | assert(pPivot && pPrev && "The pointers may not be NULL!"); |
304 | | |
305 | | // add new node |
306 | 1.16M | if( nRes < 0 ) |
307 | 323k | pPrev->m_pLeft = pIns; |
308 | 843k | else |
309 | 843k | pPrev->m_pRight = pIns; |
310 | | // rebalance tree |
311 | 1.16M | short nDelta = pPivot->Adjust( &pHeavy, pIns ); |
312 | 1.16M | if( pPivot->m_nBalance >= 2 || pPivot->m_nBalance <= -2 ) |
313 | 0 | { |
314 | 0 | StgAvlNode* pNewRoot; |
315 | 0 | pHeavy = ( nDelta < 0 ) ? pPivot->m_pRight : pPivot->m_pLeft; |
316 | | // left imbalance |
317 | 0 | if( nDelta > 0 ) |
318 | 0 | if( pHeavy->m_nBalance == 1 ) |
319 | 0 | pNewRoot = pPivot->RotLL(); |
320 | 0 | else |
321 | 0 | pNewRoot = pPivot->RotLR(); |
322 | | // right imbalance |
323 | 0 | else if( pHeavy->m_nBalance == -1 ) |
324 | 0 | pNewRoot = pPivot->RotRR(); |
325 | 0 | else |
326 | 0 | pNewRoot = pPivot->RotRL(); |
327 | | // relink balanced subtree |
328 | 0 | if( pParent == nullptr ) |
329 | 0 | *pRoot = pNewRoot; |
330 | 0 | else if( pPivot == pParent->m_pLeft ) |
331 | 0 | pParent->m_pLeft = pNewRoot; |
332 | 0 | else if( pPivot == pParent->m_pRight ) |
333 | 0 | pParent->m_pRight = pNewRoot; |
334 | 0 | } |
335 | 1.16M | return true; |
336 | 1.22M | } |
337 | | |
338 | | // Remove node from tree. Returns true is found and removed. |
339 | | // Actually delete if bDel |
340 | | |
341 | | bool StgAvlNode::Remove( StgAvlNode** pRoot, StgAvlNode* pDel, bool bDel ) |
342 | 3.46k | { |
343 | 3.46k | if ( !pRoot ) |
344 | 0 | return false; |
345 | | |
346 | | // special case - empty tree |
347 | 3.46k | if( *pRoot == nullptr ) |
348 | 0 | return false; |
349 | | // delete the element |
350 | 3.46k | pDel = Rem( pRoot, pDel, false ); |
351 | 3.46k | if( pDel ) |
352 | 3.46k | { |
353 | 3.46k | if( bDel ) |
354 | 3.46k | delete pDel; |
355 | | // Rebalance the tree the hard way |
356 | | // OS 22.09.95: On MD's request commented out due to crash |
357 | | /* StgAvlNode* pNew = NULL; |
358 | | while( *pRoot ) |
359 | | { |
360 | | StgAvlNode* p = Rem( pRoot, *pRoot, false ); |
361 | | Insert( &pNew, p ); |
362 | | } |
363 | | *pRoot = pNew;*/ |
364 | 3.46k | return true; |
365 | 3.46k | } |
366 | 0 | else |
367 | 0 | return false; |
368 | 3.46k | } |
369 | | |
370 | | // Move node to a different tree. Returns true is found and moved. This routine |
371 | | // may be called when the key has changed. |
372 | | |
373 | | |
374 | | ////////////////////////// class AvlIterator |
375 | | |
376 | | // The iterator walks through a tree one entry by one. |
377 | | |
378 | | StgAvlIterator::StgAvlIterator( StgAvlNode* p ) |
379 | 1.49M | { |
380 | 1.49M | m_pRoot = p; |
381 | 1.49M | m_nCur = 0; |
382 | 1.49M | if( p ) |
383 | 1.11M | { |
384 | 1.11M | short nCount = 0; // tree size |
385 | 1.11M | p->StgEnum( nCount ); |
386 | 1.11M | } |
387 | 1.49M | } |
388 | | |
389 | | StgAvlNode* StgAvlIterator::Find( short n ) |
390 | 6.55M | { |
391 | 6.55M | StgAvlNode* p = m_pRoot; |
392 | 21.9M | while( p ) |
393 | 20.4M | { |
394 | 20.4M | if( n == p->m_nId ) |
395 | 5.06M | break; |
396 | 15.3M | else p = ( n < p->m_nId ) ? p->m_pLeft : p->m_pRight; |
397 | 20.4M | } |
398 | 6.55M | return p; |
399 | 6.55M | } |
400 | | |
401 | | StgAvlNode* StgAvlIterator::First() |
402 | 1.49M | { |
403 | 1.49M | m_nCur = -1; |
404 | 1.49M | return Next(); |
405 | 1.49M | } |
406 | | |
407 | | StgAvlNode* StgAvlIterator::Next() |
408 | 6.55M | { |
409 | 6.55M | return Find( ++m_nCur ); |
410 | 6.55M | } |
411 | | |
412 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |