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>
25 #include "stortree.hxx"
27 #include <sal/types.h>
28 #include <sal/log.hxx>
29 #include <osl/diagnose.h>
31 #include <store/types.h>
33 #include "storbase.hxx"
34 #include "storbios.hxx"
36 using namespace store
;
38 /*========================================================================
40 * OStoreBTreeNodeData implementation.
42 *======================================================================*/
44 * OStoreBTreeNodeData.
46 OStoreBTreeNodeData::OStoreBTreeNodeData (sal_uInt16 nPageSize
)
47 : PageData (nPageSize
)
49 base::m_aGuard
.m_nMagic
= store::htonl(self::theTypeId
);
50 base::m_aDescr
.m_nUsed
= store::htons(self::thePageSize
); // usageCount(0)
51 self::m_aGuard
.m_nMagic
= store::htonl(0); // depth(0)
53 sal_uInt16
const n
= capacityCount();
56 for (sal_uInt16 i
= 1; i
< n
; i
++)
58 // cppcheck-suppress arrayIndexOutOfBounds
66 sal_uInt16
OStoreBTreeNodeData::find (const T
& t
) const
69 sal_Int32 r
= usageCount() - 1;
73 sal_Int32
const m
= ((l
+ r
) >> 1);
75 if (t
.m_aKey
== m_pData
[m
].m_aKey
)
76 return static_cast<sal_uInt16
>(m
);
77 if (t
.m_aKey
< m_pData
[m
].m_aKey
)
83 sal_uInt16
const k
= static_cast<sal_uInt16
>(r
);
84 if ((k
< capacityCount()) && (t
.m_aKey
< m_pData
[k
].m_aKey
))
93 void OStoreBTreeNodeData::insert (sal_uInt16 i
, const T
& t
)
95 sal_uInt16
const n
= usageCount();
96 sal_uInt16
const m
= capacityCount();
97 if ((n
< m
) && (i
< m
))
100 memmove (&(m_pData
[i
+ 1]), &(m_pData
[i
]), (n
- i
) * sizeof(T
));
111 void OStoreBTreeNodeData::remove (sal_uInt16 i
)
113 sal_uInt16
const n
= usageCount();
117 memmove (&(m_pData
[i
]), &(m_pData
[i
+ 1]), (n
- i
- 1) * sizeof(T
));
120 m_pData
[n
- 1] = T();
126 * split (left half copied from right half of left page).
128 void OStoreBTreeNodeData::split (const self
& rPageL
)
130 sal_uInt16
const h
= capacityCount() / 2;
131 memcpy (&(m_pData
[0]), &(rPageL
.m_pData
[h
]), h
* sizeof(T
));
138 void OStoreBTreeNodeData::truncate (sal_uInt16 n
)
140 sal_uInt16
const m
= capacityCount();
143 for (sal_uInt16 i
= n
; i
< m
; i
++)
148 /*========================================================================
150 * OStoreBTreeNodeObject implementation.
152 *======================================================================*/
156 storeError
OStoreBTreeNodeObject::guard (sal_uInt32 nAddr
)
158 return PageHolderObject
< page
>::guard (m_xPage
, nAddr
);
164 storeError
OStoreBTreeNodeObject::verify (sal_uInt32 nAddr
) const
166 return PageHolderObject
< page
>::verify (m_xPage
, nAddr
);
172 storeError
OStoreBTreeNodeObject::split (
174 PageHolderObject
< page
> & rxPageL
,
175 OStorePageBIOS
& rBIOS
)
177 PageHolderObject
< page
> xPage (m_xPage
);
179 return store_E_InvalidAccess
;
181 // Check left page usage.
183 return store_E_InvalidAccess
;
184 if (!rxPageL
->querySplit())
187 // Construct right page.
188 PageHolderObject
< page
> xPageR
;
189 if (!xPageR
.construct (rBIOS
.allocator()))
190 return store_E_OutOfMemory
;
192 // Split right page off left page.
193 xPageR
->split (*rxPageL
);
194 xPageR
->depth (rxPageL
->depth());
196 // Allocate right page.
197 self
aNodeR (xPageR
.get());
198 storeError eErrCode
= rBIOS
.allocate (aNodeR
);
199 if (eErrCode
!= store_E_None
)
202 // Truncate left page.
203 rxPageL
->truncate (rxPageL
->capacityCount() / 2);
206 self
aNodeL (rxPageL
.get());
207 eErrCode
= rBIOS
.saveObjectAt (aNodeL
, aNodeL
.location());
208 if (eErrCode
!= store_E_None
)
211 // Insert right page.
212 OStorePageLink
aLink (xPageR
->location());
213 xPage
->insert (nIndexL
+ 1, T(xPageR
->m_pData
[0].m_aKey
, aLink
));
215 // Save this page and leave.
216 return rBIOS
.saveObjectAt (*this, location());
220 * remove (down to leaf node, recursive).
222 storeError
OStoreBTreeNodeObject::remove (
224 OStoreBTreeEntry
& rEntryL
,
225 OStorePageBIOS
& rBIOS
)
227 PageHolderObject
< page
> xImpl (m_xPage
);
228 page
& rPage
= *xImpl
;
231 storeError eErrCode
= store_E_None
;
235 T
const aEntryL (rPage
.m_pData
[nIndexL
]);
236 if (rEntryL
.compare (aEntryL
) != T::COMPARE_EQUAL
)
237 return store_E_InvalidAccess
;
241 eErrCode
= rBIOS
.loadObjectAt (aNodeL
, aEntryL
.m_aLink
.location());
242 if (eErrCode
!= store_E_None
)
245 // Recurse: remove from link node.
246 eErrCode
= aNodeL
.remove (0, rEntryL
, rBIOS
);
247 if (eErrCode
!= store_E_None
)
250 // Check resulting link node usage.
251 PageHolderObject
< page
> xPageL (aNodeL
.get());
252 if (xPageL
->usageCount() == 0)
254 // Free empty link node.
255 eErrCode
= rBIOS
.free (xPageL
->location());
256 if (eErrCode
!= store_E_None
)
260 rPage
.remove (nIndexL
);
267 rPage
.m_pData
[nIndexL
].m_aKey
= xPageL
->m_pData
[0].m_aKey
;
274 if (rEntryL
.compare (rPage
.m_pData
[nIndexL
]) != T::COMPARE_EQUAL
)
275 return store_E_NotExists
;
278 rEntryL
= rPage
.m_pData
[nIndexL
];
280 // Remove leaf index.
281 rPage
.remove (nIndexL
);
285 // Check for modified node.
289 eErrCode
= rBIOS
.saveObjectAt (*this, location());
296 /*========================================================================
298 * OStoreBTreeRootObject implementation.
300 *======================================================================*/
303 * Precond: root node page loaded.
305 void OStoreBTreeRootObject::testInvariant (char const * message
) const
307 OSL_PRECOND(m_xPage
!= nullptr, "OStoreBTreeRootObject::testInvariant(): Null pointer");
308 SAL_WARN_IF( (m_xPage
->location() - m_xPage
->size()) != 0, "store", message
);
314 storeError
OStoreBTreeRootObject::loadOrCreate (
316 OStorePageBIOS
& rBIOS
)
318 storeError eErrCode
= rBIOS
.loadObjectAt (*this, nAddr
);
319 if (eErrCode
!= store_E_NotExists
)
322 eErrCode
= construct
<page
>(rBIOS
.allocator());
323 if (eErrCode
!= store_E_None
)
326 eErrCode
= rBIOS
.allocate (*this);
327 if (eErrCode
!= store_E_None
)
330 // Notify caller of the creation.
331 testInvariant("OStoreBTreeRootObject::loadOrCreate(): leave");
332 return store_E_Pending
;
338 storeError
OStoreBTreeRootObject::change (
339 PageHolderObject
< page
> & rxPageL
,
340 OStorePageBIOS
& rBIOS
)
342 PageHolderObject
< page
> xPage (m_xPage
);
343 testInvariant("OStoreBTreeRootObject::change(): enter");
345 // Save root location.
346 sal_uInt32
const nRootAddr
= xPage
->location();
348 // Construct new root.
349 if (!rxPageL
.construct (rBIOS
.allocator()))
350 return store_E_OutOfMemory
;
352 // Save this as prev root.
353 storeError eErrCode
= rBIOS
.allocate (*this);
354 if (eErrCode
!= store_E_None
)
355 return store_E_OutOfMemory
;
358 rxPageL
->depth (xPage
->depth() + 1);
359 rxPageL
->m_pData
[0] = xPage
->m_pData
[0];
360 rxPageL
->m_pData
[0].m_aLink
= xPage
->location();
361 rxPageL
->usageCount(1);
364 rxPageL
.swap (xPage
);
366 std::shared_ptr
<PageData
> tmp (xPage
.get());
370 // Save this as new root and finish.
371 eErrCode
= rBIOS
.saveObjectAt (*this, nRootAddr
);
372 testInvariant("OStoreBTreeRootObject::change(): leave");
377 * find_lookup (w/o split()).
378 * Precond: root node page loaded.
380 storeError
OStoreBTreeRootObject::find_lookup (
381 OStoreBTreeNodeObject
& rNode
, // [out]
382 sal_uInt16
& rIndex
, // [out]
383 OStorePageKey
const & rKey
,
384 OStorePageBIOS
& rBIOS
) const
386 // Init node w/ root page.
387 testInvariant("OStoreBTreeRootObject::find_lookup(): enter");
389 std::shared_ptr
<PageData
> tmp (m_xPage
);
390 tmp
.swap (rNode
.get());
393 // Setup BTree entry.
394 T
const entry (rKey
);
396 // Check current page.
397 PageHolderObject
< page
> xPage (rNode
.get());
398 for (; xPage
->depth() > 0; xPage
= rNode
.makeHolder
< page
>())
401 page
const & rPage
= *xPage
;
402 sal_uInt16
const i
= rPage
.find(entry
);
403 sal_uInt16
const n
= rPage
.usageCount();
406 // Path to entry not exists (Must not happen(?)).
407 return store_E_NotExists
;
411 sal_uInt32
const nAddr
= rPage
.m_pData
[i
].m_aLink
.location();
412 if (nAddr
== STORE_PAGE_NULL
)
414 // Path to entry not exists (Must not happen(?)).
415 return store_E_NotExists
;
419 storeError eErrCode
= rBIOS
.loadObjectAt (rNode
, nAddr
);
420 if (eErrCode
!= store_E_None
)
425 page
const & rPage
= *xPage
;
426 rIndex
= rPage
.find(entry
);
427 if (rIndex
>= rPage
.usageCount())
428 return store_E_NotExists
;
431 T::CompareResult eResult
= entry
.compare(rPage
.m_pData
[rIndex
]);
432 if (eResult
== T::COMPARE_LESS
)
434 SAL_WARN("store", "store::BTreeRoot::find_lookup(): sort error");
435 return store_E_Unknown
;
439 testInvariant("OStoreBTreeRootObject::find_lookup(): leave");
444 * find_insert (possibly with split()).
445 * Precond: root node page loaded.
447 storeError
OStoreBTreeRootObject::find_insert (
448 OStoreBTreeNodeObject
& rNode
, // [out]
449 sal_uInt16
& rIndex
, // [out]
450 OStorePageKey
const & rKey
,
451 OStorePageBIOS
& rBIOS
)
453 testInvariant("OStoreBTreeRootObject::find_insert(): enter");
455 // Check for RootNode split.
456 PageHolderObject
< page
> xRoot (m_xPage
);
457 if (xRoot
->querySplit())
459 PageHolderObject
< page
> xPageL
;
462 storeError eErrCode
= change (xPageL
, rBIOS
);
463 if (eErrCode
!= store_E_None
)
466 // Split left page (prev root).
467 eErrCode
= split (0, xPageL
, rBIOS
);
468 if (eErrCode
!= store_E_None
)
472 // Init node w/ root page.
474 std::shared_ptr
<PageData
> tmp (m_xPage
);
475 tmp
.swap (rNode
.get());
478 // Setup BTree entry.
479 T
const entry (rKey
);
481 // Check current Page.
482 PageHolderObject
< page
> xPage (rNode
.get());
483 for (; xPage
->depth() > 0; xPage
= rNode
.makeHolder
< page
>())
486 page
const & rPage
= *xPage
;
487 sal_uInt16
const i
= rPage
.find (entry
);
488 sal_uInt16
const n
= rPage
.usageCount();
491 // Path to entry not exists (Must not happen(?)).
492 return store_E_NotExists
;
496 sal_uInt32
const nAddr
= rPage
.m_pData
[i
].m_aLink
.location();
497 if (nAddr
== STORE_PAGE_NULL
)
499 // Path to entry not exists (Must not happen(?)).
500 return store_E_NotExists
;
504 OStoreBTreeNodeObject aNext
;
505 storeError eErrCode
= rBIOS
.loadObjectAt (aNext
, nAddr
);
506 if (eErrCode
!= store_E_None
)
509 // Check for next node split.
510 PageHolderObject
< page
> xNext (aNext
.get());
511 if (xNext
->querySplit())
514 eErrCode
= rNode
.split (i
, xNext
, rBIOS
);
515 if (eErrCode
!= store_E_None
)
522 // Let next page be current.
523 std::shared_ptr
<PageData
> tmp (aNext
.get());
524 tmp
.swap (rNode
.get());
528 page
const & rPage
= *xPage
;
529 rIndex
= rPage
.find(entry
);
530 if (rIndex
< rPage
.usageCount())
533 T::CompareResult result
= entry
.compare (rPage
.m_pData
[rIndex
]);
534 if (result
== T::COMPARE_LESS
)
536 SAL_WARN("store", "store::BTreeRoot::find_insert(): sort error");
537 return store_E_Unknown
;
540 if (result
== T::COMPARE_EQUAL
)
541 return store_E_AlreadyExists
;
544 // Greater or not (yet) existing.
545 testInvariant("OStoreBTreeRootObject::find_insert(): leave");
549 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */