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 .
20 #include <sal/config.h>
24 #include "stortree.hxx"
26 #include "sal/types.h"
27 #include "sal/log.hxx"
28 #include "osl/diagnose.h"
30 #include "store/types.h"
32 #include "storbase.hxx"
33 #include "storbios.hxx"
35 using namespace store
;
37 /*========================================================================
39 * OStoreBTreeNodeData implementation.
41 *======================================================================*/
43 * OStoreBTreeNodeData.
45 OStoreBTreeNodeData::OStoreBTreeNodeData (sal_uInt16 nPageSize
)
46 : PageData (nPageSize
)
48 base::m_aGuard
.m_nMagic
= store::htonl(self::theTypeId
);
49 base::m_aDescr
.m_nUsed
= store::htons(self::thePageSize
); // usageCount(0)
50 self::m_aGuard
.m_nMagic
= store::htonl(0); // depth(0)
52 sal_uInt16
const n
= capacityCount();
55 for (sal_uInt16 i
= 1; i
< n
; i
++)
57 // cppcheck-suppress arrayIndexOutOfBounds
65 sal_uInt16
OStoreBTreeNodeData::find (const T
& t
) const
68 sal_Int32 r
= usageCount() - 1;
72 sal_Int32
const m
= ((l
+ r
) >> 1);
74 if (t
.m_aKey
== m_pData
[m
].m_aKey
)
76 if (t
.m_aKey
< m_pData
[m
].m_aKey
)
82 sal_uInt16
const k
= ((sal_uInt16
)(r
));
83 if ((k
< capacityCount()) && (t
.m_aKey
< m_pData
[k
].m_aKey
))
92 void OStoreBTreeNodeData::insert (sal_uInt16 i
, const T
& t
)
94 sal_uInt16
const n
= usageCount();
95 sal_uInt16
const m
= capacityCount();
96 if ((n
< m
) && (i
< m
))
99 memmove (&(m_pData
[i
+ 1]), &(m_pData
[i
]), (n
- i
) * sizeof(T
));
110 void OStoreBTreeNodeData::remove (sal_uInt16 i
)
112 sal_uInt16
const n
= usageCount();
116 memmove (&(m_pData
[i
]), &(m_pData
[i
+ 1]), (n
- i
- 1) * sizeof(T
));
119 m_pData
[n
- 1] = T();
125 * split (left half copied from right half of left page).
127 void OStoreBTreeNodeData::split (const self
& rPageL
)
129 sal_uInt16
const h
= capacityCount() / 2;
130 memcpy (&(m_pData
[0]), &(rPageL
.m_pData
[h
]), h
* sizeof(T
));
137 void OStoreBTreeNodeData::truncate (sal_uInt16 n
)
139 sal_uInt16
const m
= capacityCount();
142 for (sal_uInt16 i
= n
; i
< m
; i
++)
147 /*========================================================================
149 * OStoreBTreeNodeObject implementation.
151 *======================================================================*/
155 storeError
OStoreBTreeNodeObject::guard (sal_uInt32 nAddr
)
157 return PageHolderObject
< page
>::guard (m_xPage
, nAddr
);
163 storeError
OStoreBTreeNodeObject::verify (sal_uInt32 nAddr
) const
165 return PageHolderObject
< page
>::verify (m_xPage
, nAddr
);
171 storeError
OStoreBTreeNodeObject::split (
173 PageHolderObject
< page
> & rxPageL
,
174 OStorePageBIOS
& rBIOS
)
176 PageHolderObject
< page
> xPage (m_xPage
);
178 return store_E_InvalidAccess
;
180 // Check left page usage.
182 return store_E_InvalidAccess
;
183 if (!rxPageL
->querySplit())
186 // Construct right page.
187 PageHolderObject
< page
> xPageR
;
188 if (!xPageR
.construct (rBIOS
.allocator()))
189 return store_E_OutOfMemory
;
191 // Split right page off left page.
192 xPageR
->split (*rxPageL
);
193 xPageR
->depth (rxPageL
->depth());
195 // Allocate right page.
196 self
aNodeR (xPageR
.get());
197 storeError eErrCode
= rBIOS
.allocate (aNodeR
);
198 if (eErrCode
!= store_E_None
)
201 // Truncate left page.
202 rxPageL
->truncate (rxPageL
->capacityCount() / 2);
205 self
aNodeL (rxPageL
.get());
206 eErrCode
= rBIOS
.saveObjectAt (aNodeL
, aNodeL
.location());
207 if (eErrCode
!= store_E_None
)
210 // Insert right page.
211 OStorePageLink
aLink (xPageR
->location());
212 xPage
->insert (nIndexL
+ 1, T(xPageR
->m_pData
[0].m_aKey
, aLink
));
214 // Save this page and leave.
215 return rBIOS
.saveObjectAt (*this, location());
219 * remove (down to leaf node, recursive).
221 storeError
OStoreBTreeNodeObject::remove (
223 OStoreBTreeEntry
& rEntryL
,
224 OStorePageBIOS
& rBIOS
)
226 PageHolderObject
< page
> xImpl (m_xPage
);
227 page
& rPage
= (*xImpl
);
230 storeError eErrCode
= store_E_None
;
234 T
const aEntryL (rPage
.m_pData
[nIndexL
]);
235 if (!(rEntryL
.compare (aEntryL
) == T::COMPARE_EQUAL
))
236 return store_E_InvalidAccess
;
240 eErrCode
= rBIOS
.loadObjectAt (aNodeL
, aEntryL
.m_aLink
.location());
241 if (eErrCode
!= store_E_None
)
244 // Recurse: remove from link node.
245 eErrCode
= aNodeL
.remove (0, rEntryL
, rBIOS
);
246 if (eErrCode
!= store_E_None
)
249 // Check resulting link node usage.
250 PageHolderObject
< page
> xPageL (aNodeL
.get());
251 if (xPageL
->usageCount() == 0)
253 // Free empty link node.
254 eErrCode
= rBIOS
.free (xPageL
->location());
255 if (eErrCode
!= store_E_None
)
259 rPage
.remove (nIndexL
);
266 rPage
.m_pData
[nIndexL
].m_aKey
= xPageL
->m_pData
[0].m_aKey
;
273 if (!(rEntryL
.compare (rPage
.m_pData
[nIndexL
]) == T::COMPARE_EQUAL
))
274 return store_E_NotExists
;
277 rEntryL
= rPage
.m_pData
[nIndexL
];
279 // Remove leaf index.
280 rPage
.remove (nIndexL
);
284 // Check for modified node.
288 eErrCode
= rBIOS
.saveObjectAt (*this, location());
295 /*========================================================================
297 * OStoreBTreeRootObject implementation.
299 *======================================================================*/
302 * Precond: root node page loaded.
304 void OStoreBTreeRootObject::testInvariant (char const * message
)
306 OSL_PRECOND(m_xPage
.get() != nullptr, "OStoreBTreeRootObject::testInvariant(): Null pointer");
307 SAL_WARN_IF( (m_xPage
->location() - m_xPage
->size()) != 0, "store", message
);
313 storeError
OStoreBTreeRootObject::loadOrCreate (
315 OStorePageBIOS
& rBIOS
)
317 storeError eErrCode
= rBIOS
.loadObjectAt (*this, nAddr
);
318 if (eErrCode
!= store_E_NotExists
)
321 eErrCode
= construct
<page
>(rBIOS
.allocator());
322 if (eErrCode
!= store_E_None
)
325 eErrCode
= rBIOS
.allocate (*this);
326 if (eErrCode
!= store_E_None
)
329 // Notify caller of the creation.
330 testInvariant("OStoreBTreeRootObject::loadOrCreate(): leave");
331 return store_E_Pending
;
337 storeError
OStoreBTreeRootObject::change (
338 PageHolderObject
< page
> & rxPageL
,
339 OStorePageBIOS
& rBIOS
)
341 PageHolderObject
< page
> xPage (m_xPage
);
342 testInvariant("OStoreBTreeRootObject::change(): enter");
344 // Save root location.
345 sal_uInt32
const nRootAddr
= xPage
->location();
347 // Construct new root.
348 if (!rxPageL
.construct (rBIOS
.allocator()))
349 return store_E_OutOfMemory
;
351 // Save this as prev root.
352 storeError eErrCode
= rBIOS
.allocate (*this);
353 if (eErrCode
!= store_E_None
)
354 return store_E_OutOfMemory
;
357 rxPageL
->depth (xPage
->depth() + 1);
358 rxPageL
->m_pData
[0] = xPage
->m_pData
[0];
359 rxPageL
->m_pData
[0].m_aLink
= xPage
->location();
360 rxPageL
->usageCount(1);
363 rxPageL
.swap (xPage
);
365 std::shared_ptr
<PageData
> tmp (xPage
.get());
369 // Save this as new root and finish.
370 eErrCode
= rBIOS
.saveObjectAt (*this, nRootAddr
);
371 testInvariant("OStoreBTreeRootObject::change(): leave");
376 * find_lookup (w/o split()).
377 * Precond: root node page loaded.
379 storeError
OStoreBTreeRootObject::find_lookup (
380 OStoreBTreeNodeObject
& rNode
, // [out]
381 sal_uInt16
& rIndex
, // [out]
382 OStorePageKey
const & rKey
,
383 OStorePageBIOS
& rBIOS
)
385 // Init node w/ root page.
386 testInvariant("OStoreBTreeRootObject::find_lookup(): enter");
388 std::shared_ptr
<PageData
> tmp (m_xPage
);
389 tmp
.swap (rNode
.get());
392 // Setup BTree entry.
393 T
const entry (rKey
);
395 // Check current page.
396 PageHolderObject
< page
> xPage (rNode
.get());
397 for (; xPage
->depth() > 0; xPage
= rNode
.makeHolder
< page
>())
400 page
const & rPage
= (*xPage
);
401 sal_uInt16
const i
= rPage
.find(entry
);
402 sal_uInt16
const n
= rPage
.usageCount();
405 // Path to entry not exists (Must not happen(?)).
406 return store_E_NotExists
;
410 sal_uInt32
const nAddr
= rPage
.m_pData
[i
].m_aLink
.location();
411 if (nAddr
== STORE_PAGE_NULL
)
413 // Path to entry not exists (Must not happen(?)).
414 return store_E_NotExists
;
418 storeError eErrCode
= rBIOS
.loadObjectAt (rNode
, nAddr
);
419 if (eErrCode
!= store_E_None
)
424 page
const & rPage
= (*xPage
);
425 rIndex
= rPage
.find(entry
);
426 if (!(rIndex
< rPage
.usageCount()))
427 return store_E_NotExists
;
430 T::CompareResult eResult
= entry
.compare(rPage
.m_pData
[rIndex
]);
431 if (eResult
== T::COMPARE_LESS
)
433 SAL_WARN("store", "store::BTreeRoot::find_lookup(): sort error");
434 return store_E_Unknown
;
438 testInvariant("OStoreBTreeRootObject::find_lookup(): leave");
443 * find_insert (possibly with split()).
444 * Precond: root node page loaded.
446 storeError
OStoreBTreeRootObject::find_insert (
447 OStoreBTreeNodeObject
& rNode
, // [out]
448 sal_uInt16
& rIndex
, // [out]
449 OStorePageKey
const & rKey
,
450 OStorePageBIOS
& rBIOS
)
452 testInvariant("OStoreBTreeRootObject::find_insert(): enter");
454 // Check for RootNode split.
455 PageHolderObject
< page
> xRoot (m_xPage
);
456 if (xRoot
->querySplit())
458 PageHolderObject
< page
> xPageL
;
461 storeError eErrCode
= change (xPageL
, rBIOS
);
462 if (eErrCode
!= store_E_None
)
465 // Split left page (prev root).
466 eErrCode
= split (0, xPageL
, rBIOS
);
467 if (eErrCode
!= store_E_None
)
471 // Init node w/ root page.
473 std::shared_ptr
<PageData
> tmp (m_xPage
);
474 tmp
.swap (rNode
.get());
477 // Setup BTree entry.
478 T
const entry (rKey
);
480 // Check current Page.
481 PageHolderObject
< page
> xPage (rNode
.get());
482 for (; xPage
->depth() > 0; xPage
= rNode
.makeHolder
< page
>())
485 page
const & rPage
= (*xPage
);
486 sal_uInt16
const i
= rPage
.find (entry
);
487 sal_uInt16
const n
= rPage
.usageCount();
490 // Path to entry not exists (Must not happen(?)).
491 return store_E_NotExists
;
495 sal_uInt32
const nAddr
= rPage
.m_pData
[i
].m_aLink
.location();
496 if (nAddr
== STORE_PAGE_NULL
)
498 // Path to entry not exists (Must not happen(?)).
499 return store_E_NotExists
;
503 OStoreBTreeNodeObject aNext
;
504 storeError eErrCode
= rBIOS
.loadObjectAt (aNext
, nAddr
);
505 if (eErrCode
!= store_E_None
)
508 // Check for next node split.
509 PageHolderObject
< page
> xNext (aNext
.get());
510 if (xNext
->querySplit())
513 eErrCode
= rNode
.split (i
, xNext
, rBIOS
);
514 if (eErrCode
!= store_E_None
)
521 // Let next page be current.
522 std::shared_ptr
<PageData
> tmp (aNext
.get());
523 tmp
.swap (rNode
.get());
527 page
const & rPage
= (*xPage
);
528 rIndex
= rPage
.find(entry
);
529 if (rIndex
< rPage
.usageCount())
532 T::CompareResult result
= entry
.compare (rPage
.m_pData
[rIndex
]);
533 if (result
== T::COMPARE_LESS
)
535 SAL_WARN("store", "store::BTreeRoot::find_insert(): sort error");
536 return store_E_Unknown
;
539 if (result
== T::COMPARE_EQUAL
)
540 return store_E_AlreadyExists
;
543 // Greater or not (yet) existing.
544 testInvariant("OStoreBTreeRootObject::find_insert(): leave");
548 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */