blob: e6adacfef1d03cbde9e2e2d504a3f1f45d00317a [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_store.hxx"
#include "stortree.hxx"
#include "sal/types.h"
#include "osl/diagnose.h"
#include "store/types.h"
#include "storbase.hxx"
#include "storbios.hxx"
using namespace store;
/*========================================================================
*
* OStoreBTreeNodeData implementation.
*
*======================================================================*/
/*
* OStoreBTreeNodeData.
*/
OStoreBTreeNodeData::OStoreBTreeNodeData (sal_uInt16 nPageSize)
: OStorePageData (nPageSize)
{
base::m_aGuard.m_nMagic = store::htonl(self::theTypeId);
base::m_aDescr.m_nUsed = store::htons(self::thePageSize); // usageCount(0)
self::m_aGuard.m_nMagic = store::htonl(0); // depth(0)
sal_uInt16 const n = capacityCount();
T const t;
for (sal_uInt16 i = 1; i < n; i++)
m_pData[i] = t;
}
/*
* find.
*/
sal_uInt16 OStoreBTreeNodeData::find (const T& t) const
{
register sal_Int32 l = 0;
register sal_Int32 r = usageCount() - 1;
while (l < r)
{
register sal_Int32 const m = ((l + r) >> 1);
if (t.m_aKey == m_pData[m].m_aKey)
return ((sal_uInt16)(m));
if (t.m_aKey < m_pData[m].m_aKey)
r = m - 1;
else
l = m + 1;
}
sal_uInt16 const k = ((sal_uInt16)(r));
if ((k < capacityCount()) && (t.m_aKey < m_pData[k].m_aKey))
return(k - 1);
else
return(k);
}
/*
* insert.
*/
void OStoreBTreeNodeData::insert (sal_uInt16 i, const T& t)
{
sal_uInt16 const n = usageCount();
sal_uInt16 const m = capacityCount();
if ((n < m) && (i < m))
{
// shift right.
memmove (&(m_pData[i + 1]), &(m_pData[i]), (n - i) * sizeof(T));
// insert.
m_pData[i] = t;
usageCount (n + 1);
}
}
/*
* remove.
*/
void OStoreBTreeNodeData::remove (sal_uInt16 i)
{
sal_uInt16 const n = usageCount();
if (i < n)
{
// shift left.
memmove (&(m_pData[i]), &(m_pData[i + 1]), (n - i - 1) * sizeof(T));
// truncate.
m_pData[n - 1] = T();
usageCount (n - 1);
}
}
#if 0 /* NYI */
/*
* merge (with right page).
*/
void OStoreBTreeNodeData::merge (const self& rPageR)
{
sal_uInt16 const n = usageCount();
sal_uInt16 const m = rPageR.usageCount();
if ((n + m) <= capacityCount())
{
memcpy (&(m_pData[n]), &(rPageR.m_pData[0]), m * sizeof(T));
usageCount (n + m);
}
}
#endif
/*
* split (left half copied from right half of left page).
*/
void OStoreBTreeNodeData::split (const self& rPageL)
{
sal_uInt16 const h = capacityCount() / 2;
memcpy (&(m_pData[0]), &(rPageL.m_pData[h]), h * sizeof(T));
truncate (h);
}
/*
* truncate.
*/
void OStoreBTreeNodeData::truncate (sal_uInt16 n)
{
sal_uInt16 const m = capacityCount();
T const t;
for (sal_uInt16 i = n; i < m; i++)
m_pData[i] = t;
usageCount (n);
}
/*========================================================================
*
* OStoreBTreeNodeObject implementation.
*
*======================================================================*/
/*
* guard.
*/
storeError OStoreBTreeNodeObject::guard (sal_uInt32 nAddr)
{
return PageHolderObject< page >::guard (m_xPage, nAddr);
}
/*
* verify.
*/
storeError OStoreBTreeNodeObject::verify (sal_uInt32 nAddr) const
{
return PageHolderObject< page >::verify (m_xPage, nAddr);
}
/*
* split.
*/
storeError OStoreBTreeNodeObject::split (
sal_uInt16 nIndexL,
PageHolderObject< page > & rxPageL,
OStorePageBIOS & rBIOS)
{
PageHolderObject< page > xPage (m_xPage);
if (!xPage.is())
return store_E_InvalidAccess;
// Check left page usage.
if (!rxPageL.is())
return store_E_InvalidAccess;
if (!rxPageL->querySplit())
return store_E_None;
// Construct right page.
PageHolderObject< page > xPageR;
if (!xPageR.construct (rBIOS.allocator()))
return store_E_OutOfMemory;
// Split right page off left page.
xPageR->split (*rxPageL);
xPageR->depth (rxPageL->depth());
// Allocate right page.
self aNodeR (xPageR.get());
storeError eErrCode = rBIOS.allocate (aNodeR);
if (eErrCode != store_E_None)
return eErrCode;
// Truncate left page.
rxPageL->truncate (rxPageL->capacityCount() / 2);
// Save left page.
self aNodeL (rxPageL.get());
eErrCode = rBIOS.saveObjectAt (aNodeL, aNodeL.location());
if (eErrCode != store_E_None)
return eErrCode;
// Insert right page.
OStorePageLink aLink (xPageR->location());
xPage->insert (nIndexL + 1, T(xPageR->m_pData[0].m_aKey, aLink));
// Save this page and leave.
return rBIOS.saveObjectAt (*this, location());
}
/*
* remove (down to leaf node, recursive).
*/
storeError OStoreBTreeNodeObject::remove (
sal_uInt16 nIndexL,
OStoreBTreeEntry & rEntryL,
OStorePageBIOS & rBIOS)
{
PageHolderObject< page > xImpl (m_xPage);
page & rPage = (*xImpl);
// Check depth.
storeError eErrCode = store_E_None;
if (rPage.depth())
{
// Check link entry.
T const aEntryL (rPage.m_pData[nIndexL]);
if (!(rEntryL.compare (aEntryL) == T::COMPARE_EQUAL))
return store_E_InvalidAccess;
// Load link node.
self aNodeL;
eErrCode = rBIOS.loadObjectAt (aNodeL, aEntryL.m_aLink.location());
if (eErrCode != store_E_None)
return eErrCode;
// Recurse: remove from link node.
eErrCode = aNodeL.remove (0, rEntryL, rBIOS);
if (eErrCode != store_E_None)
return eErrCode;
// Check resulting link node usage.
PageHolderObject< page > xPageL (aNodeL.get());
if (xPageL->usageCount() == 0)
{
// Free empty link node.
eErrCode = rBIOS.free (xPageL->location());
if (eErrCode != store_E_None)
return eErrCode;
// Remove index.
rPage.remove (nIndexL);
touch();
}
else
{
#if 0 /* NYI */
// Check for right sibling.
sal_uInt16 const nIndexR = nIndexL + 1;
if (nIndexR < rPage.usageCount())
{
// Load right link node.
self aNodeR;
eErrCode = rBIOS.loadObjectAt (aNodeR, rPage.m_pData[nIndexR].m_aLink.location());
if (eErrCode == store_E_None)
{
if (rPageL.queryMerge (rPageR))
{
rPageL.merge (rPageR);
eErrCode = rBIOS.free (rPageR.location());
}
}
}
#endif /* NYI */
// Relink.
rPage.m_pData[nIndexL].m_aKey = xPageL->m_pData[0].m_aKey;
touch();
}
}
else
{
// Check leaf entry.
if (!(rEntryL.compare (rPage.m_pData[nIndexL]) == T::COMPARE_EQUAL))
return store_E_NotExists;
// Save leaf entry.
rEntryL = rPage.m_pData[nIndexL];
// Remove leaf index.
rPage.remove (nIndexL);
touch();
}
// Check for modified node.
if (dirty())
{
// Save this page.
eErrCode = rBIOS.saveObjectAt (*this, location());
}
// Done.
return eErrCode;
}
/*========================================================================
*
* OStoreBTreeRootObject implementation.
*
*======================================================================*/
/*
* testInvariant.
* Precond: root node page loaded.
*/
bool OStoreBTreeRootObject::testInvariant (char const * message)
{
OSL_PRECOND(m_xPage.get() != 0, "OStoreBTreeRootObject::testInvariant(): Null pointer");
bool result = ((m_xPage->location() - m_xPage->size()) == 0);
OSL_POSTCOND(result, message); (void) message;
return result;
}
/*
* loadOrCreate.
*/
storeError OStoreBTreeRootObject::loadOrCreate (
sal_uInt32 nAddr,
OStorePageBIOS & rBIOS)
{
storeError eErrCode = rBIOS.loadObjectAt (*this, nAddr);
if (eErrCode != store_E_NotExists)
return eErrCode;
eErrCode = construct<page>(rBIOS.allocator());
if (eErrCode != store_E_None)
return eErrCode;
eErrCode = rBIOS.allocate (*this);
if (eErrCode != store_E_None)
return eErrCode;
// Notify caller of the creation.
(void) testInvariant("OStoreBTreeRootObject::loadOrCreate(): leave");
return store_E_Pending;
}
/*
* change.
*/
storeError OStoreBTreeRootObject::change (
PageHolderObject< page > & rxPageL,
OStorePageBIOS & rBIOS)
{
PageHolderObject< page > xPage (m_xPage);
(void) testInvariant("OStoreBTreeRootObject::change(): enter");
// Save root location.
sal_uInt32 const nRootAddr = xPage->location();
// Construct new root.
if (!rxPageL.construct (rBIOS.allocator()))
return store_E_OutOfMemory;
// Save this as prev root.
storeError eErrCode = rBIOS.allocate (*this);
if (eErrCode != store_E_None)
return store_E_OutOfMemory;
// Setup new root.
rxPageL->depth (xPage->depth() + 1);
rxPageL->m_pData[0] = xPage->m_pData[0];
rxPageL->m_pData[0].m_aLink = xPage->location();
rxPageL->usageCount(1);
// Change root.
rxPageL.swap (xPage);
{
PageHolder tmp (xPage.get());
tmp.swap (m_xPage);
}
// Save this as new root and finish.
eErrCode = rBIOS.saveObjectAt (*this, nRootAddr);
(void) testInvariant("OStoreBTreeRootObject::change(): leave");
return eErrCode;
}
/*
* find_lookup (w/o split()).
* Precond: root node page loaded.
*/
storeError OStoreBTreeRootObject::find_lookup (
OStoreBTreeNodeObject & rNode, // [out]
sal_uInt16 & rIndex, // [out]
OStorePageKey const & rKey,
OStorePageBIOS & rBIOS)
{
// Init node w/ root page.
(void) testInvariant("OStoreBTreeRootObject::find_lookup(): enter");
{
PageHolder tmp (m_xPage);
tmp.swap (rNode.get());
}
// Setup BTree entry.
T const entry (rKey);
// Check current page.
PageHolderObject< page > xPage (rNode.get());
for (; xPage->depth() > 0; xPage = rNode.makeHolder< page >())
{
// Find next page.
page const & rPage = (*xPage);
sal_uInt16 const i = rPage.find(entry);
sal_uInt16 const n = rPage.usageCount();
if (!(i < n))
{
// Path to entry not exists (Must not happen(?)).
return store_E_NotExists;
}
// Check address.
sal_uInt32 const nAddr = rPage.m_pData[i].m_aLink.location();
if (nAddr == STORE_PAGE_NULL)
{
// Path to entry not exists (Must not happen(?)).
return store_E_NotExists;
}
// Load next page.
storeError eErrCode = rBIOS.loadObjectAt (rNode, nAddr);
if (eErrCode != store_E_None)
return eErrCode;
}
// Find index.
page const & rPage = (*xPage);
rIndex = rPage.find(entry);
if (!(rIndex < rPage.usageCount()))
return store_E_NotExists;
// Compare entry.
T::CompareResult eResult = entry.compare(rPage.m_pData[rIndex]);
OSL_POSTCOND(eResult != T::COMPARE_LESS, "store::BTreeRoot::find_lookup(): sort error");
if (eResult == T::COMPARE_LESS)
return store_E_Unknown;
// Greater or Equal.
(void) testInvariant("OStoreBTreeRootObject::find_lookup(): leave");
return store_E_None;
}
/*
* find_insert (possibly with split()).
* Precond: root node page loaded.
*/
storeError OStoreBTreeRootObject::find_insert (
OStoreBTreeNodeObject & rNode, // [out]
sal_uInt16 & rIndex, // [out]
OStorePageKey const & rKey,
OStorePageBIOS & rBIOS)
{
(void) testInvariant("OStoreBTreeRootObject::find_insert(): enter");
// Check for RootNode split.
PageHolderObject< page > xRoot (m_xPage);
if (xRoot->querySplit())
{
PageHolderObject< page > xPageL;
// Change root.
storeError eErrCode = change (xPageL, rBIOS);
if (eErrCode != store_E_None)
return eErrCode;
// Split left page (prev root).
eErrCode = split (0, xPageL, rBIOS);
if (eErrCode != store_E_None)
return eErrCode;
}
// Init node w/ root page.
{
PageHolder tmp (m_xPage);
tmp.swap (rNode.get());
}
// Setup BTree entry.
T const entry (rKey);
// Check current Page.
PageHolderObject< page > xPage (rNode.get());
for (; xPage->depth() > 0; xPage = rNode.makeHolder< page >())
{
// Find next page.
page const & rPage = (*xPage);
sal_uInt16 const i = rPage.find (entry);
sal_uInt16 const n = rPage.usageCount();
if (!(i < n))
{
// Path to entry not exists (Must not happen(?)).
return store_E_NotExists;
}
// Check address.
sal_uInt32 const nAddr = rPage.m_pData[i].m_aLink.location();
if (nAddr == STORE_PAGE_NULL)
{
// Path to entry not exists (Must not happen(?)).
return store_E_NotExists;
}
// Load next page.
OStoreBTreeNodeObject aNext;
storeError eErrCode = rBIOS.loadObjectAt (aNext, nAddr);
if (eErrCode != store_E_None)
return eErrCode;
// Check for next node split.
PageHolderObject< page > xNext (aNext.get());
if (xNext->querySplit())
{
// Split next node.
eErrCode = rNode.split (i, xNext, rBIOS);
if (eErrCode != store_E_None)
return eErrCode;
// Restart.
continue;
}
// Let next page be current.
PageHolder tmp (aNext.get());
tmp.swap (rNode.get());
}
// Find index.
page const & rPage = (*xPage);
rIndex = rPage.find(entry);
if (rIndex < rPage.usageCount())
{
// Compare entry.
T::CompareResult result = entry.compare (rPage.m_pData[rIndex]);
OSL_POSTCOND(result != T::COMPARE_LESS, "store::BTreeRoot::find_insert(): sort error");
if (result == T::COMPARE_LESS)
return store_E_Unknown;
if (result == T::COMPARE_EQUAL)
return store_E_AlreadyExists;
}
// Greater or not (yet) existing.
(void) testInvariant("OStoreBTreeRootObject::find_insert(): leave");
return store_E_None;
}