/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: stordata.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-20 00:33:11 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#define _STORE_STORDATA_CXX_ "$Revision: 1.4 $"

#ifndef _SAL_TYPES_H_
#include <sal/types.h>
#endif

#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif
#ifndef _OSL_ENDIAN_H_
#include <osl/endian.h>
#endif
#ifndef _OSL_MUTEX_HXX_
#include <osl/mutex.hxx>
#endif

#ifndef _STORE_TYPES_H_
#include <store/types.h>
#endif

#ifndef _STORE_STORBASE_HXX_
#include <storbase.hxx>
#endif
#ifndef _STORE_STORDATA_HXX_
#include <stordata.hxx>
#endif

using namespace store;

/*========================================================================
 *
 * OStoreIndirectionPageData implementation.
 *
 *======================================================================*/
/*
 * OStoreIndirectionPageData.
 */
OStoreIndirectionPageData::OStoreIndirectionPageData (sal_uInt16 nPageSize)
    : OStorePageData (nPageSize)
{
    initialize();
}

/*
 * initialize.
 */
void OStoreIndirectionPageData::initialize (void)
{
    base::m_aGuard.m_nMagic = STORE_MAGIC_INDIRECTPAGE;
    base::m_aDescr.m_nUsed = sal::static_int_cast< sal_uInt16 >(
        base::m_aDescr.m_nUsed + self::size());
    self::m_aGuard.m_nMagic = 0;

    sal_uInt16 i, n = capacityCount();
    for (i = 0; i < n; i++)
        m_pData[i] = STORE_PAGE_NULL;
}

/*
 * swap.
 */
void OStoreIndirectionPageData::swap (
    const D&
#ifdef OSL_BIGENDIAN
        rDescr
#endif /* OSL_BIGENDIAN */
)
{
#ifdef OSL_BIGENDIAN
    m_aGuard.swap();

    sal_uInt16 i, n = capacityCount (rDescr);
    for (i = 0; i < n; i++)
        m_pData[i] = OSL_SWAPDWORD(m_pData[i]);
#endif /* OSL_BIGENDIAN */
}

/*========================================================================
 *
 * OStoreIndirectionPageObject implementation.
 *
 *======================================================================*/
/*
 * swap.
 */
void OStoreIndirectionPageObject::swap (
    const D&
#ifdef OSL_BIGENDIAN
        rDescr
#endif /* OSL_BIGENDIAN */
)
{
#ifdef OSL_BIGENDIAN
    base::swap (rDescr);
    m_rPage.swap (rDescr);
#endif /* OSL_BIGENDIAN */
}

/*
 * guard.
 */
void OStoreIndirectionPageObject::guard (const D& rDescr)
{
    base::guard (rDescr);
    m_rPage.guard (rDescr);
}

/*
 * verify.
 */
storeError OStoreIndirectionPageObject::verify (const D& rDescr)
{
    storeError eErrCode = base::verify (rDescr);
    if (eErrCode != store_E_None)
        return eErrCode;
    else
        return m_rPage.verify (rDescr);
}

/*
 * get (single indirect).
 */
storeError OStoreIndirectionPageObject::get (
    sal_uInt16             nSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Check arguments.
    if (!(nSingle < m_rPage.capacityCount()))
        STORE_METHOD_LEAVE(pMutex, store_E_InvalidAccess);

    // Obtain data page location.
    sal_uInt32 nAddr = m_rPage.m_pData[nSingle];
    if (nAddr == STORE_PAGE_NULL)
        STORE_METHOD_LEAVE(pMutex, store_E_NotExists);

    // Load data page.
    rData.location (nAddr);
    storeError eErrCode = rBIOS.load (rData);

    // Leave.
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * get (double indirect).
 */
storeError OStoreIndirectionPageObject::get (
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    page                 *&rpSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Check arguments.
    if (!((nDouble < m_rPage.capacityCount()) &&
          (nSingle < m_rPage.capacityCount())    ))
        STORE_METHOD_LEAVE(pMutex, store_E_InvalidAccess);

    // Check single indirect page location.
    if (m_rPage.m_pData[nDouble] == STORE_PAGE_NULL)
        STORE_METHOD_LEAVE(pMutex, store_E_NotExists);

    // Check single indirect page buffer.
    if (rpSingle == NULL)
    {
        sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
        rpSingle = new(nPageSize) page(nPageSize);
    }

    // Load single indirect page.
    OStoreIndirectionPageObject aSingle (*rpSingle);
    aSingle.location (m_rPage.m_pData[nDouble]);

    storeError eErrCode = rBIOS.load (aSingle);
    if (eErrCode != store_E_None)
        STORE_METHOD_LEAVE(pMutex, eErrCode);

    // Get single indirect.
    eErrCode = aSingle.get (nSingle, rData, rBIOS, NULL);

    // Leave.
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * get (triple indirect).
 */
storeError OStoreIndirectionPageObject::get (
    sal_uInt16             nTriple,
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    page                 *&rpDouble,
    page                 *&rpSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Check arguments.
    if (!((nTriple < m_rPage.capacityCount()) &&
          (nDouble < m_rPage.capacityCount()) &&
          (nSingle < m_rPage.capacityCount())    ))
        STORE_METHOD_LEAVE(pMutex, store_E_InvalidAccess);

    // Check double indirect page location.
    if (m_rPage.m_pData[nTriple] == STORE_PAGE_NULL)
        STORE_METHOD_LEAVE(pMutex, store_E_NotExists);

    // Check double indirect page buffer.
    if (rpDouble == NULL)
    {
        sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
        rpDouble = new(nPageSize) page(nPageSize);
    }

    // Load double indirect page.
    OStoreIndirectionPageObject aDouble (*rpDouble);
    aDouble.location (m_rPage.m_pData[nTriple]);

    storeError eErrCode = rBIOS.load (aDouble);
    if (eErrCode != store_E_None)
        STORE_METHOD_LEAVE(pMutex, eErrCode);

    // Get double indirect.
    eErrCode = aDouble.get (nDouble, nSingle, rpSingle, rData, rBIOS, NULL);

    // Leave.
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * put (single indirect).
 */
storeError OStoreIndirectionPageObject::put (
    sal_uInt16             nSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Check arguments.
    storeError eErrCode = store_E_InvalidAccess;
    if (!(nSingle < m_rPage.capacityCount()))
        STORE_METHOD_LEAVE(pMutex, eErrCode);

    // Obtain data page location.
    rData.location (m_rPage.m_pData[nSingle]);
    if (rData.location() == STORE_PAGE_NULL)
    {
        // Allocate data page.
        eErrCode = rBIOS.allocate (rData);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Save data page location.
        m_rPage.m_pData[nSingle] = rData.location();
        touch();

        // Save this page.
        eErrCode = rBIOS.save (*this);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);
    }
    else
    {
        // Save data page.
        eErrCode = rBIOS.save (rData);
    }

    // Leave.
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * put (double indirect).
 */
storeError OStoreIndirectionPageObject::put (
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    page                 *&rpSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Check arguments.
    storeError eErrCode = store_E_InvalidAccess;
    if (!((nDouble < m_rPage.capacityCount()) &&
          (nSingle < m_rPage.capacityCount())    ))
        STORE_METHOD_LEAVE(pMutex, eErrCode);

    // Check single indirect page buffer.
    if (rpSingle == NULL)
    {
        sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
        rpSingle = new(nPageSize) page(nPageSize);
    }

    // Obtain single indirect page location.
    OStoreIndirectionPageObject aSingle (*rpSingle);
    aSingle.location (m_rPage.m_pData[nDouble]);
    if (aSingle.location() == STORE_PAGE_NULL)
    {
        // Initialize single indirect page buffer.
        rpSingle->initialize();

        // Allocate single indirect page.
        eErrCode = rBIOS.allocate (aSingle);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Save single indirect page location.
        m_rPage.m_pData[nDouble] = aSingle.location();
        touch();

        // Save this page.
        eErrCode = rBIOS.save (*this);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);
    }
    else
    {
        // Load single indirect page.
        eErrCode = rBIOS.load (aSingle);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);
    }

    // Put single indirect.
    eErrCode = aSingle.put (nSingle, rData, rBIOS, NULL);

    // Leave.
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * put (triple indirect).
 */
storeError OStoreIndirectionPageObject::put (
    sal_uInt16             nTriple,
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    page                 *&rpDouble,
    page                 *&rpSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Check arguments.
    storeError eErrCode = store_E_InvalidAccess;
    if (!((nTriple < m_rPage.capacityCount()) &&
          (nDouble < m_rPage.capacityCount()) &&
          (nSingle < m_rPage.capacityCount())    ))
        STORE_METHOD_LEAVE(pMutex, eErrCode);

    // Check double indirect page buffer.
    if (rpDouble == NULL)
    {
        sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
        rpDouble = new(nPageSize) page(nPageSize);
    }

    // Obtain double indirect page location.
    OStoreIndirectionPageObject aDouble (*rpDouble);
    aDouble.location (m_rPage.m_pData[nTriple]);
    if (aDouble.location() == STORE_PAGE_NULL)
    {
        // Initialize double indirect page buffer.
        rpDouble->initialize();

        // Allocate double indirect page.
        eErrCode = rBIOS.allocate (aDouble);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Save double indirect page location.
        m_rPage.m_pData[nTriple] = aDouble.location();
        touch();

        // Save this page.
        eErrCode = rBIOS.save (*this);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);
    }
    else
    {
        // Load double indirect page.
        eErrCode = rBIOS.load (aDouble);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);
    }

    // Put double indirect.
    eErrCode = aDouble.put (nDouble, nSingle, rpSingle, rData, rBIOS, NULL);

    // Leave.
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * truncate (single indirect).
 */
storeError OStoreIndirectionPageObject::truncate (
    sal_uInt16             nSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Check arguments.
    sal_uInt16 i, n = m_rPage.capacityCount();
    if (!(nSingle < n))
        STORE_METHOD_LEAVE(pMutex, store_E_InvalidAccess);

    // Save PageDescriptor.
    D aDescr (m_rPage.m_aDescr);

    // Acquire Lock.
    storeError eErrCode = rBIOS.acquireLock (aDescr.m_nAddr, aDescr.m_nSize);
    if (eErrCode != store_E_None)
        STORE_METHOD_LEAVE(pMutex, eErrCode);

    // Truncate.
    for (i = n; i > nSingle; i--)
    {
        // Obtain data page location.
        sal_uInt32 nAddr = m_rPage.m_pData[i - 1];
        if (nAddr == STORE_PAGE_NULL) continue;

        // Free data page.
        rData.location (nAddr);
        eErrCode = rBIOS.free (rData);
        if (eErrCode != store_E_None)
        {
            rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
            STORE_METHOD_LEAVE(pMutex, eErrCode);
        }

        // Clear pointer to data page.
        m_rPage.m_pData[i - 1] = STORE_PAGE_NULL;
        touch();
    }

    // Check for modified page.
    if (dirty())
    {
        // Save this page.
        eErrCode = rBIOS.save (*this);
        if (eErrCode != store_E_None)
        {
            // Must not happen.
            OSL_TRACE("OStoreIndirectionPageObject::truncate(): save failed");

            // Release Lock and Leave.
            rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
            STORE_METHOD_LEAVE(pMutex, eErrCode);
        }
    }

    // Release Lock and Leave.
    eErrCode = rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * truncate (double indirect).
 */
storeError OStoreIndirectionPageObject::truncate (
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    page                 *&rpSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Acquire Mutex.
    STORE_METHOD_ENTER(pMutex);

    // Check arguments.
    if (!((nDouble < m_rPage.capacityCount()) &&
          (nSingle < m_rPage.capacityCount())    ))
        STORE_METHOD_LEAVE(pMutex, store_E_InvalidAccess);

    // Save PageDescriptor.
    D aDescr (m_rPage.m_aDescr);

    // Acquire Lock.
    storeError eErrCode = rBIOS.acquireLock (aDescr.m_nAddr, aDescr.m_nSize);
    if (eErrCode != store_E_None)
        STORE_METHOD_LEAVE(pMutex, eErrCode);

    // Truncate.
    sal_uInt16 i, n = m_rPage.capacityCount();
    for (i = n; i > nDouble + 1; i--)
    {
        // Obtain single indirect page location.
        sal_uInt32 nAddr = m_rPage.m_pData[i - 1];
        if (nAddr == STORE_PAGE_NULL) continue;

        // Check single indirect page buffer.
        if (rpSingle == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpSingle = new(nPageSize) page(nPageSize);
        }

        // Load single indirect page.
        OStoreIndirectionPageObject aSingle (*rpSingle);
        aSingle.location (nAddr);

        eErrCode = rBIOS.load (aSingle);
        if (eErrCode == store_E_None)
        {
            // Truncate to zero direct pages.
            eErrCode = aSingle.truncate (0, rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }

            // Free single indirect page.
            eErrCode = rBIOS.free (aSingle);
            if (eErrCode != store_E_None)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }
        }
        else
        {
            if (eErrCode != store_E_InvalidChecksum)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }
        }

        // Clear pointer to single indirect page.
        m_rPage.m_pData[i - 1] = STORE_PAGE_NULL;
        touch();
    }

    // Obtain last single indirect page location.
    sal_uInt32 nAddr = m_rPage.m_pData[nDouble];
    if (nAddr != STORE_PAGE_NULL)
    {
        // Check single indirect page buffer.
        if (rpSingle == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpSingle = new(nPageSize) page(nPageSize);
        }

        // Load last single indirect page.
        OStoreIndirectionPageObject aSingle (*rpSingle);
        aSingle.location (nAddr);

        eErrCode = rBIOS.load (aSingle);
        if (eErrCode == store_E_None)
        {
            // Truncate to 'nSingle' direct pages.
            eErrCode = aSingle.truncate (nSingle, rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }
        }
        else
        {
            if (eErrCode != store_E_InvalidChecksum)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }
        }

        // Check for complete truncation.
        if (nSingle == 0)
        {
            // Free last single indirect page.
            eErrCode = rBIOS.free (aSingle);
            if (eErrCode != store_E_None)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }

            // Clear pointer to last single indirect page.
            m_rPage.m_pData[nDouble] = STORE_PAGE_NULL;
            touch();
        }
    }

    // Check for modified page.
    if (dirty())
    {
        // Save this page.
        eErrCode = rBIOS.save (*this);
        if (eErrCode != store_E_None)
        {
            // Must not happen.
            OSL_TRACE("OStoreIndirectionPageObject::truncate(): save failed");

            // Release Lock and Leave.
            rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
            STORE_METHOD_LEAVE(pMutex, eErrCode);
        }
    }

    // Release Lock and Leave.
    eErrCode = rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * truncate (triple indirect).
 */
storeError OStoreIndirectionPageObject::truncate (
    sal_uInt16             nTriple,
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    page                 *&rpDouble,
    page                 *&rpSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Check arguments.
    if (!((nTriple < m_rPage.capacityCount()) &&
          (nDouble < m_rPage.capacityCount()) &&
          (nSingle < m_rPage.capacityCount())    ))
        STORE_METHOD_LEAVE(pMutex, store_E_InvalidAccess);

    // Save PageDescriptor.
    D aDescr (m_rPage.m_aDescr);

    // Acquire Lock.
    storeError eErrCode = rBIOS.acquireLock (aDescr.m_nAddr, aDescr.m_nSize);
    if (eErrCode != store_E_None)
        STORE_METHOD_LEAVE(pMutex, eErrCode);

    // Truncate.
    sal_uInt16 i, n = m_rPage.capacityCount();
    for (i = n; i > nTriple + 1; i--)
    {
        // Obtain double indirect page location.
        sal_uInt32 nAddr = m_rPage.m_pData[i - 1];
        if (nAddr == STORE_PAGE_NULL) continue;

        // Check double indirect page buffer.
        if (rpDouble == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpDouble = new(nPageSize) page(nPageSize);
        }

        // Load double indirect page.
        OStoreIndirectionPageObject aDouble (*rpDouble);
        aDouble.location (nAddr);

        eErrCode = rBIOS.load (aDouble);
        if (eErrCode == store_E_None)
        {
            // Truncate to zero single indirect pages.
            eErrCode = aDouble.truncate (
                0, 0, rpSingle, rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }

            // Free double indirect page.
            eErrCode = rBIOS.free (aDouble);
            if (eErrCode != store_E_None)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }
        }
        else
        {
            if (eErrCode != store_E_InvalidChecksum)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }
        }

        // Clear pointer to double indirect page.
        m_rPage.m_pData[i - 1] = STORE_PAGE_NULL;
        touch();
    }

    // Obtain last double indirect page location.
    sal_uInt32 nAddr = m_rPage.m_pData[nTriple];
    if (nAddr != STORE_PAGE_NULL)
    {
        // Check double indirect page buffer.
        if (rpDouble == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpDouble = new(nPageSize) page(nPageSize);
        }

        // Load last double indirect page.
        OStoreIndirectionPageObject aDouble (*rpDouble);
        aDouble.location (nAddr);

        eErrCode = rBIOS.load (aDouble);
        if (eErrCode == store_E_None)
        {
            // Truncate to 'nDouble', 'nSingle' pages.
            eErrCode = aDouble.truncate (
                nDouble, nSingle, rpSingle, rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }
        }
        else
        {
            if (eErrCode != store_E_InvalidChecksum)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }
        }

        // Check for complete truncation.
        if ((nDouble + nSingle) == 0)
        {
            // Free last double indirect page.
            eErrCode = rBIOS.free (aDouble);
            if (eErrCode != store_E_None)
            {
                rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
                STORE_METHOD_LEAVE(pMutex, eErrCode);
            }

            // Clear pointer to last double indirect page.
            m_rPage.m_pData[nTriple] = STORE_PAGE_NULL;
            touch();
        }
    }

    // Check for modified page.
    if (dirty())
    {
        // Save this page.
        eErrCode = rBIOS.save (*this);
        if (eErrCode != store_E_None)
        {
            // Must not happen.
            OSL_TRACE("OStoreIndirectionPageObject::truncate(): save failed");

            // Release Lock and Leave.
            rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
            STORE_METHOD_LEAVE(pMutex, eErrCode);
        }
    }

    // Release Lock and Leave.
    eErrCode = rBIOS.releaseLock (aDescr.m_nAddr, aDescr.m_nSize);
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*========================================================================
 *
 * OStoreDirectoryDataBlock::LinkTable implementation.
 *
 *======================================================================*/
/*
 * LinkTable::LinkTable.
 */
OStoreDirectoryDataBlock::LinkTable::LinkTable (void)
{
    initialize();
}

/*
 * LinkTable::initialize.
 */
void OStoreDirectoryDataBlock::LinkTable::initialize (void)
{
    sal_Int32 i;
    for (i = 0; i < STORE_LIMIT_DATAPAGE_DIRECT; i++)
        m_pDirect[i] = STORE_PAGE_NULL;
    for (i = 0; i < STORE_LIMIT_DATAPAGE_SINGLE; i++)
        m_pSingle[i] = STORE_PAGE_NULL;
    for (i = 0; i < STORE_LIMIT_DATAPAGE_DOUBLE; i++)
        m_pDouble[i] = STORE_PAGE_NULL;
    for (i = 0; i < STORE_LIMIT_DATAPAGE_TRIPLE; i++)
        m_pTriple[i] = STORE_PAGE_NULL;
}

/*
 * LinkTable::swap.
 */
void OStoreDirectoryDataBlock::LinkTable::swap (void)
{
#ifdef OSL_BIGENDIAN
    sal_Int32 i;
    for (i = 0; i < STORE_LIMIT_DATAPAGE_DIRECT; i++)
        m_pDirect[i] = OSL_SWAPDWORD(m_pDirect[i]);
    for (i = 0; i < STORE_LIMIT_DATAPAGE_SINGLE; i++)
        m_pSingle[i] = OSL_SWAPDWORD(m_pSingle[i]);
    for (i = 0; i < STORE_LIMIT_DATAPAGE_DOUBLE; i++)
        m_pDouble[i] = OSL_SWAPDWORD(m_pDouble[i]);
    for (i = 0; i < STORE_LIMIT_DATAPAGE_TRIPLE; i++)
        m_pTriple[i] = OSL_SWAPDWORD(m_pTriple[i]);
#endif /* OSL_BIGENDIAN */
}

/*========================================================================
 *
 * OStoreDirectoryPageObject implementation.
 *
 *======================================================================*/
/*
 * swap.
 */
void OStoreDirectoryPageObject::swap (
    const D&
#ifdef OSL_BIGENDIAN
        rDescr
#endif /* OSL_BIGENDIAN */
)
{
#ifdef OSL_BIGENDIAN
    base::swap (rDescr);
    m_rPage.swap ();
#endif /* OSL_BIGENDIAN */
}

/*
 * guard.
 */
void OStoreDirectoryPageObject::guard (const D& rDescr)
{
    base::guard (rDescr);
    m_rPage.guard ();
}

/*
 * verify.
 */
storeError OStoreDirectoryPageObject::verify (const D& rDescr)
{
    storeError eErrCode = base::verify (rDescr);
    if (eErrCode != store_E_None)
        return eErrCode;
    else
        return m_rPage.verify ();
}

/*
 * scope (external data page).
 */
OStoreDirectoryPageData::ChunkScope
OStoreDirectoryPageObject::scope (
    sal_uInt32                       nPage,
    page::DataBlock::LinkDescriptor &rDescr) const
{
    typedef OStoreIndirectionPageData indrct;
    sal_uInt32 index0, index1, index2, index3;

    // direct.
    sal_uInt32 nCount = m_rPage.m_aDataBlock.directCount();
    sal_uInt32 nLimit = nCount;
    if (nPage < nLimit)
    {
        // Page to index reduction.
        index0 = nPage;

        // Setup LinkDescriptor indices.
        rDescr.m_nIndex0 = (sal_uInt16)(index0 & 0xffff);

        // Done.
        return page::SCOPE_DIRECT;
    }
    nPage -= nLimit;

    // single indirect.
    sal_uInt32 nCapacity = indrct::capacityCount(m_rPage.m_aDescr);
    nCount = m_rPage.m_aDataBlock.singleCount();
    nLimit = nCount * nCapacity;
    if (nPage < nLimit)
    {
        // Page to index reduction.
        sal_uInt32 n = nPage;

        // Reduce to single indirect i(1), direct n = i(0).
        index1 = n / nCapacity;
        index0 = n % nCapacity;

        // Verify reduction.
        n = index1 * nCapacity + index0;
        OSL_POSTCOND(n == nPage, "wrong math on indirect indices");
        if (n != nPage)
            return page::SCOPE_UNKNOWN;

        // Setup LinkDescriptor indices.
        rDescr.m_nIndex0 = (sal_uInt16)(index0 & 0xffff);
        rDescr.m_nIndex1 = (sal_uInt16)(index1 & 0xffff);

        // Done.
        return page::SCOPE_SINGLE;
    }
    nPage -= nLimit;

    // double indirect.
    nCount = m_rPage.m_aDataBlock.doubleCount();
    nLimit = nCount * nCapacity * nCapacity;
    if (nPage < nLimit)
    {
        // Page to index reduction.
        sal_uInt32 n = nPage;

        // Reduce to double indirect i(2), single indirect n = i(0).
        index2 = n / (nCapacity * nCapacity);
        n      = n % (nCapacity * nCapacity);

        // Reduce to single indirect i(1), direct n = i(0).
        index1 = n / nCapacity;
        index0 = n % nCapacity;

        // Verify reduction.
        n = index2 * nCapacity * nCapacity +
            index1 * nCapacity + index0;
        OSL_POSTCOND(n == nPage, "wrong math on double indirect indices");
        if (n != nPage)
            return page::SCOPE_UNKNOWN;

        // Setup LinkDescriptor indices.
        rDescr.m_nIndex0 = (sal_uInt16)(index0 & 0xffff);
        rDescr.m_nIndex1 = (sal_uInt16)(index1 & 0xffff);
        rDescr.m_nIndex2 = (sal_uInt16)(index2 & 0xffff);

        // Done.
        return page::SCOPE_DOUBLE;
    }
    nPage -= nLimit;

    // triple indirect.
    nCount = m_rPage.m_aDataBlock.tripleCount();
    nLimit = nCount * nCapacity * nCapacity * nCapacity;
    if (nPage < nLimit)
    {
        // Page to index reduction.
        sal_uInt32 n = nPage;

        // Reduce to triple indirect i(3), double indirect n.
        index3 = n / (nCapacity * nCapacity * nCapacity);
        n      = n % (nCapacity * nCapacity * nCapacity);

        // Reduce to double indirect i(2), single indirect n.
        index2 = n / (nCapacity * nCapacity);
        n      = n % (nCapacity * nCapacity);

        // Reduce to single indirect i(1), direct n = i(0).
        index1 = n / nCapacity;
        index0 = n % nCapacity;

        // Verify reduction.
        n = index3 * nCapacity * nCapacity * nCapacity +
            index2 * nCapacity * nCapacity +
            index1 * nCapacity + index0;
        OSL_POSTCOND(n == nPage, "wrong math on triple indirect indices");
        if (n != nPage)
            return page::SCOPE_UNKNOWN;

        // Setup LinkDescriptor indices.
        rDescr.m_nIndex0 = (sal_uInt16)(index0 & 0xffff);
        rDescr.m_nIndex1 = (sal_uInt16)(index1 & 0xffff);
        rDescr.m_nIndex2 = (sal_uInt16)(index2 & 0xffff);
        rDescr.m_nIndex3 = (sal_uInt16)(index3 & 0xffff);

        // Done.
        return page::SCOPE_TRIPLE;
    }

    // Unreachable (more than triple indirect).
    return page::SCOPE_UNREACHABLE;
}

/*
 * get (external data page).
 */
storeError OStoreDirectoryPageObject::get (
    sal_uInt32             nPage,
    indirect             *&rpSingle,
    indirect             *&rpDouble,
    indirect             *&rpTriple,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Determine scope and link indices.
    page::DataBlock::LinkDescriptor aLink;
    page::ChunkScope eScope = scope (nPage, aLink);

    storeError eErrCode = store_E_None;
    if (eScope == page::SCOPE_DIRECT)
    {
        sal_uInt32 nAddr = directLink (aLink.m_nIndex0);
        if (nAddr == STORE_PAGE_NULL)
            STORE_METHOD_LEAVE(pMutex, store_E_NotExists);

        rData.location (nAddr);
        eErrCode = rBIOS.load (rData);
    }
    else if (eScope == page::SCOPE_SINGLE)
    {
        sal_uInt32 nAddr = singleLink (aLink.m_nIndex1);
        if (nAddr == STORE_PAGE_NULL)
            STORE_METHOD_LEAVE(pMutex, store_E_NotExists);

        if (rpSingle == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpSingle = new(nPageSize) indirect(nPageSize);
        }

        OStoreIndirectionPageObject aSingle (*rpSingle);
        aSingle.location (nAddr);

        eErrCode = rBIOS.load (aSingle);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        eErrCode = aSingle.get (
            aLink.m_nIndex0,
            rData, rBIOS, NULL);
    }
    else if (eScope == page::SCOPE_DOUBLE)
    {
        sal_uInt32 nAddr = doubleLink (aLink.m_nIndex2);
        if (nAddr == STORE_PAGE_NULL)
            STORE_METHOD_LEAVE(pMutex, store_E_NotExists);

        if (rpDouble == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpDouble = new(nPageSize) indirect(nPageSize);
        }

        OStoreIndirectionPageObject aDouble (*rpDouble);
        aDouble.location (nAddr);

        eErrCode = rBIOS.load (aDouble);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        eErrCode = aDouble.get (
            aLink.m_nIndex1,
            aLink.m_nIndex0,
            rpSingle,
            rData, rBIOS, NULL);
    }
    else if (eScope == page::SCOPE_TRIPLE)
    {
        sal_uInt32 nAddr = tripleLink (aLink.m_nIndex3);
        if (nAddr == STORE_PAGE_NULL)
            STORE_METHOD_LEAVE(pMutex, store_E_NotExists);

        if (rpTriple == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpTriple = new(nPageSize) indirect(nPageSize);
        }

        OStoreIndirectionPageObject aTriple (*rpTriple);
        aTriple.location (nAddr);

        eErrCode = rBIOS.load (aTriple);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        eErrCode = aTriple.get (
            aLink.m_nIndex2,
            aLink.m_nIndex1,
            aLink.m_nIndex0,
            rpDouble,
            rpSingle,
            rData, rBIOS, NULL);
    }
    else if (eScope == page::SCOPE_UNREACHABLE)
    {
        // Out of scope.
        eErrCode = store_E_CantSeek;
    }
    else
    {
        // Unknown scope.
        OSL_TRACE("OStoreDirectoryPageObject::get(): scope failed");
        eErrCode = store_E_Unknown;
    }

    // Leave.
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * put (external data page).
 */
storeError OStoreDirectoryPageObject::put (
    sal_uInt32             nPage,
    indirect             *&rpSingle,
    indirect             *&rpDouble,
    indirect             *&rpTriple,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Determine scope and link indices.
    page::DataBlock::LinkDescriptor aLink;
    page::ChunkScope eScope = scope (nPage, aLink);

    storeError eErrCode = store_E_None;
    if (eScope == page::SCOPE_DIRECT)
    {
        rData.location (directLink (aLink.m_nIndex0));
        if (rData.location() == STORE_PAGE_NULL)
        {
            eErrCode = rBIOS.allocate (rData);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            directLink (aLink.m_nIndex0, rData.location());
        }
        else
        {
            eErrCode = rBIOS.save (rData);
        }
    }
    else if (eScope == page::SCOPE_SINGLE)
    {
        if (rpSingle == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpSingle = new(nPageSize) indirect(nPageSize);
        }

        OStoreIndirectionPageObject aSingle (*rpSingle);
        aSingle.location (singleLink (aLink.m_nIndex1));
        if (aSingle.location() == STORE_PAGE_NULL)
        {
            rpSingle->initialize();

            eErrCode = rBIOS.allocate (aSingle);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            singleLink (aLink.m_nIndex1, aSingle.location());
        }
        else
        {
            eErrCode = rBIOS.load (aSingle);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);
        }

        eErrCode = aSingle.put (
            aLink.m_nIndex0,
            rData, rBIOS, NULL);
    }
    else if (eScope == page::SCOPE_DOUBLE)
    {
        if (rpDouble == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpDouble = new(nPageSize) indirect(nPageSize);
        }

        OStoreIndirectionPageObject aDouble (*rpDouble);
        aDouble.location (doubleLink (aLink.m_nIndex2));
        if (aDouble.location() == STORE_PAGE_NULL)
        {
            rpDouble->initialize();

            eErrCode = rBIOS.allocate (aDouble);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            doubleLink (aLink.m_nIndex2, aDouble.location());
        }
        else
        {
            eErrCode = rBIOS.load (aDouble);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);
        }

        eErrCode = aDouble.put (
            aLink.m_nIndex1,
            aLink.m_nIndex0,
            rpSingle,
            rData, rBIOS, NULL);
    }
    else if (eScope == page::SCOPE_TRIPLE)
    {
        if (rpTriple == NULL)
        {
            sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
            rpTriple = new(nPageSize) indirect(nPageSize);
        }

        OStoreIndirectionPageObject aTriple (*rpTriple);
        aTriple.location (tripleLink (aLink.m_nIndex3));
        if (aTriple.location() == STORE_PAGE_NULL)
        {
            rpTriple->initialize();

            eErrCode = rBIOS.allocate (aTriple);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            tripleLink (aLink.m_nIndex3, aTriple.location());
        }
        else
        {
            eErrCode = rBIOS.load (aTriple);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);
        }

        eErrCode = aTriple.put (
            aLink.m_nIndex2,
            aLink.m_nIndex1,
            aLink.m_nIndex0,
            rpDouble,
            rpSingle,
            rData, rBIOS, NULL);
    }
    else if (eScope == page::SCOPE_UNREACHABLE)
    {
        // Out of scope.
        eErrCode = store_E_CantSeek;
    }
    else
    {
        // Unknown scope.
        OSL_TRACE("OStoreDirectoryPageObject::put(): scope failed");
        eErrCode = store_E_Unknown;
    }

    // Leave.
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * truncate (external data page).
 */
storeError OStoreDirectoryPageObject::truncate (
    sal_uInt32             nPage,
    indirect             *&rpSingle,
    indirect             *&rpDouble,
    indirect             *&rpTriple,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    // Determine scope and link indices.
    page::DataBlock::LinkDescriptor aLink;
    page::ChunkScope eScope = scope (nPage, aLink);

    storeError eErrCode = store_E_None;
    if (eScope == page::SCOPE_DIRECT)
    {
        // Truncate all triple indirect pages.
        eErrCode = truncate (
            page::SCOPE_TRIPLE, 0,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Truncate all double indirect pages.
        eErrCode = truncate (
            page::SCOPE_DOUBLE, 0,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Truncate all single indirect pages.
        eErrCode = truncate (
            page::SCOPE_SINGLE, 0,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Truncate direct pages, including 'aLink.m_nIndex0'.
        eErrCode = truncate (
            eScope, aLink.m_nIndex0,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
    }
    else if (eScope == page::SCOPE_SINGLE)
    {
        // Truncate all triple indirect pages.
        eErrCode = truncate (
            page::SCOPE_TRIPLE, 0,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Truncate all double indirect pages.
        eErrCode = truncate (
            page::SCOPE_DOUBLE, 0,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Truncate single indirect pages, downto 'aLink.m_nIndex1'.
        eErrCode = truncate (
            eScope, aLink.m_nIndex1 + 1,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Obtain last single indirect page location.
        sal_uInt32 nAddr = singleLink (aLink.m_nIndex1);
        if (nAddr != STORE_PAGE_NULL)
        {
            // Check single indirect page buffer.
            if (rpSingle == NULL)
            {
                sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
                rpSingle = new(nPageSize) indirect(nPageSize);
            }

            // Load last single indirect page.
            OStoreIndirectionPageObject aSingle (*rpSingle);
            aSingle.location (nAddr);

            eErrCode = rBIOS.load (aSingle);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            // Truncate to ... pages.
            eErrCode = aSingle.truncate (
                aLink.m_nIndex0,
                rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            // Check for complete truncation.
            if (aLink.m_nIndex0 == 0)
            {
                // Free last single indirect page.
                eErrCode = rBIOS.free (aSingle);
                if (eErrCode != store_E_None)
                    STORE_METHOD_LEAVE(pMutex, eErrCode);

                // Clear pointer to last single indirect page.
                singleLink (aLink.m_nIndex1, STORE_PAGE_NULL);
            }
        }
    }
    else if (eScope == page::SCOPE_DOUBLE)
    {
        // Truncate all triple indirect pages.
        eErrCode = truncate (
            page::SCOPE_TRIPLE, 0,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Truncate double indirect pages, downto 'aLink.m_nIndex2'.
        eErrCode = truncate (
            eScope, aLink.m_nIndex2 + 1,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Obtain last double indirect page location.
        sal_uInt32 nAddr = doubleLink (aLink.m_nIndex2);
        if (nAddr != STORE_PAGE_NULL)
        {
            // Check double indirect page buffer.
            if (rpDouble == NULL)
            {
                sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
                rpDouble = new(nPageSize) indirect(nPageSize);
            }

            // Load last double indirect page.
            OStoreIndirectionPageObject aDouble (*rpDouble);
            aDouble.location (nAddr);

            eErrCode = rBIOS.load (aDouble);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            // Truncate to ... pages.
            eErrCode = aDouble.truncate (
                aLink.m_nIndex1,
                aLink.m_nIndex0,
                rpSingle,
                rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            // Check for complete truncation.
            if ((aLink.m_nIndex1 + aLink.m_nIndex0) == 0)
            {
                // Free last double indirect page.
                eErrCode = rBIOS.free (aDouble);
                if (eErrCode != store_E_None)
                    STORE_METHOD_LEAVE(pMutex, eErrCode);

                // Clear pointer to last double indirect page.
                doubleLink (aLink.m_nIndex2, STORE_PAGE_NULL);
            }
        }
    }
    else if (eScope == page::SCOPE_TRIPLE)
    {
        // Truncate triple indirect pages, downto 'aLink.m_nIndex3'.
        eErrCode = truncate (
            eScope, aLink.m_nIndex3 + 1,
            rpSingle, rpDouble, rpTriple,
            rData, rBIOS, NULL);
        if (eErrCode != store_E_None)
            STORE_METHOD_LEAVE(pMutex, eErrCode);

        // Obtain last triple indirect page location.
        sal_uInt32 nAddr = tripleLink (aLink.m_nIndex3);
        if (nAddr != STORE_PAGE_NULL)
        {
            // Check triple indirect page buffer.
            if (rpTriple == NULL)
            {
                sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
                rpTriple = new(nPageSize) indirect(nPageSize);
            }

            // Load last triple indirect page.
            OStoreIndirectionPageObject aTriple (*rpTriple);
            aTriple.location (nAddr);

            eErrCode = rBIOS.load (aTriple);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            // Truncate to ... pages.
            eErrCode = aTriple.truncate (
                aLink.m_nIndex2,
                aLink.m_nIndex1,
                aLink.m_nIndex0,
                rpDouble, rpSingle,
                rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
                STORE_METHOD_LEAVE(pMutex, eErrCode);

            // Check for complete truncation.
            if ((aLink.m_nIndex2 + aLink.m_nIndex1 + aLink.m_nIndex0) == 0)
            {
                // Free last triple indirect page.
                eErrCode = rBIOS.free (aTriple);
                if (eErrCode != store_E_None)
                    STORE_METHOD_LEAVE(pMutex, eErrCode);

                // Clear pointer to last triple indirect page.
                tripleLink (aLink.m_nIndex3, STORE_PAGE_NULL);
            }
        }
    }
    else if (eScope == page::SCOPE_UNREACHABLE)
    {
        // Out of scope.
        eErrCode = store_E_CantSeek;
    }
    else
    {
        // Unknown scope.
        OSL_TRACE("OStoreDirectoryPageObject::put(): scope failed");
        eErrCode = store_E_Unknown;
    }

    // Leave.
    STORE_METHOD_LEAVE(pMutex, eErrCode);
}

/*
 * truncate (external data page scope; private).
 */
storeError OStoreDirectoryPageObject::truncate (
    page::ChunkScope       eScope,
    sal_uInt16             nRemain,
    indirect             *&rpSingle,
    indirect             *&rpDouble,
    indirect             *&rpTriple,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS,
    osl::Mutex            *pMutex)
{
    // Enter.
    STORE_METHOD_ENTER(pMutex);

    storeError eErrCode = store_E_None;
    if (eScope == page::SCOPE_DIRECT)
    {
        // Truncate direct data pages.
        sal_uInt16 i, n = m_rPage.m_aDataBlock.directCount();
        for (i = n; i > nRemain; i--)
        {
            // Obtain data page location.
            sal_uInt32 nAddr = directLink (i - 1);
            if (nAddr == STORE_PAGE_NULL) continue;

            // Free data page.
            rData.location (nAddr);
            eErrCode = rBIOS.free (rData);
            if (eErrCode != store_E_None)
                break;

            // Clear pointer to data page.
            directLink (i - 1, STORE_PAGE_NULL);
        }

        // Done.
        STORE_METHOD_LEAVE(pMutex, eErrCode);
    }

    if (eScope == page::SCOPE_SINGLE)
    {
        // Truncate single indirect pages.
        sal_uInt16 i, n = m_rPage.m_aDataBlock.singleCount();
        for (i = n; i > nRemain; i--)
        {
            // Obtain single indirect page location.
            sal_uInt32 nAddr = singleLink (i - 1);
            if (nAddr == STORE_PAGE_NULL) continue;

            // Check single indirect page buffer.
            if (rpSingle == NULL)
            {
                sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
                rpSingle = new(nPageSize) indirect(nPageSize);
            }

            OStoreIndirectionPageObject aSingle (*rpSingle);
            aSingle.location (nAddr);

            // Load single indirect page.
            eErrCode = rBIOS.load (aSingle);
            if (eErrCode != store_E_None)
                break;

            // Truncate to zero data pages.
            eErrCode = aSingle.truncate (
                0, rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
                break;

            // Free single indirect page.
            eErrCode = rBIOS.free (aSingle);
            if (eErrCode != store_E_None)
                break;

            // Clear pointer to single indirect page.
            singleLink (i - 1, STORE_PAGE_NULL);
        }

        // Done.
        STORE_METHOD_LEAVE(pMutex, eErrCode);
    }

    if (eScope == page::SCOPE_DOUBLE)
    {
        // Truncate double indirect pages.
        sal_uInt16 i, n = m_rPage.m_aDataBlock.doubleCount();
        for (i = n; i > nRemain; i--)
        {
            // Obtain double indirect page location.
            sal_uInt32 nAddr = doubleLink (i - 1);
            if (nAddr == STORE_PAGE_NULL) continue;

            // Check double indirect page buffer.
            if (rpDouble == NULL)
            {
                sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
                rpDouble = new(nPageSize) indirect(nPageSize);
            }

            OStoreIndirectionPageObject aDouble (*rpDouble);
            aDouble.location (nAddr);

            // Load double indirect page.
            eErrCode = rBIOS.load (aDouble);
            if (eErrCode != store_E_None)
                break;

            // Truncate to zero single indirect pages.
            eErrCode = aDouble.truncate (
                0, 0, rpSingle, rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
                break;

            // Free double indirect page.
            eErrCode = rBIOS.free (aDouble);
            if (eErrCode != store_E_None)
                break;

            // Clear pointer to double indirect page.
            doubleLink (i - 1, STORE_PAGE_NULL);
        }

        // Done.
        STORE_METHOD_LEAVE(pMutex, eErrCode);
    }

    if (eScope == page::SCOPE_TRIPLE)
    {
        // Truncate triple indirect pages.
        sal_uInt16 i, n = m_rPage.m_aDataBlock.tripleCount();
        for (i = n; i > nRemain; i--)
        {
            // Obtain triple indirect page location.
            sal_uInt32 nAddr = tripleLink (i - 1);
            if (nAddr == STORE_PAGE_NULL) continue;

            // Check triple indirect page buffer.
            if (rpTriple == NULL)
            {
                sal_uInt16 nPageSize = m_rPage.m_aDescr.m_nSize;
                rpTriple = new(nPageSize) indirect(nPageSize);
            }

            OStoreIndirectionPageObject aTriple (*rpTriple);
            aTriple.location (nAddr);

            // Load triple indirect page.
            eErrCode = rBIOS.load (aTriple);
            if (eErrCode != store_E_None)
                break;

            // Truncate to zero double indirect pages.
            eErrCode = aTriple.truncate (
                0, 0, 0, rpDouble, rpSingle, rData, rBIOS, NULL);
            if (eErrCode != store_E_None)
                break;

            // Free triple indirect page.
            eErrCode = rBIOS.free (aTriple);
            if (eErrCode != store_E_None)
                break;

            // Clear pointer to triple indirect page.
            tripleLink (i - 1, STORE_PAGE_NULL);
        }

        // Done.
        STORE_METHOD_LEAVE(pMutex, eErrCode);
    }

    // Invalid scope.
    STORE_METHOD_LEAVE(pMutex, store_E_InvalidAccess);
}

