/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_store.hxx"

#include "stordata.hxx"

#include "sal/types.h"
#include "osl/diagnose.h"

#include "store/types.h"
#include "storbase.hxx"
#include "storbios.hxx"

using namespace store;

/*========================================================================
 *
 * OStoreDataPageObject implementation.
 *
 *======================================================================*/
/*
 * guard.
 */
storeError OStoreDataPageObject::guard (sal_uInt32 nAddr)
{
    return PageHolderObject< page >::guard (m_xPage, nAddr);
}

/*
 * verify.
 */
storeError OStoreDataPageObject::verify (sal_uInt32 nAddr) const
{
    return PageHolderObject< page >::verify (m_xPage, nAddr);
}

/*========================================================================
 *
 * OStoreIndirectionPageObject implementation.
 *
 *======================================================================*/
/*
  * store_truncate_Impl (single indirect page).
  */
static storeError store_truncate_Impl (
    sal_uInt32      nAddr,
    sal_uInt16      nSingle,
    OStorePageBIOS &rBIOS)
{
    if (nAddr != STORE_PAGE_NULL)
    {
        // Load single indirect page.
        OStoreIndirectionPageObject aSingle;
        storeError eErrCode = rBIOS.loadObjectAt (aSingle, nAddr);
        if (eErrCode == store_E_None)
        {
            // Truncate to 'nSingle' direct pages.
            eErrCode = aSingle.truncate (nSingle, rBIOS);
            if (eErrCode != store_E_None)
                return eErrCode;
        }
        else
        {
            if (eErrCode != store_E_InvalidChecksum)
                return eErrCode;
        }

        // Check for complete truncation.
        if (nSingle == 0)
        {
            // Free single indirect page.
            eErrCode = rBIOS.free (nAddr);
            if (eErrCode != store_E_None)
                return eErrCode;
        }
    }
    return store_E_None;
}

/*
 * store_truncate_Impl (double indirect page).
 */
static storeError store_truncate_Impl (
    sal_uInt32       nAddr,
    sal_uInt16       nDouble,
    sal_uInt16       nSingle,
    OStorePageBIOS  &rBIOS)
{
    if (nAddr != STORE_PAGE_NULL)
    {
        // Load double indirect page.
        OStoreIndirectionPageObject aDouble;
        storeError eErrCode = rBIOS.loadObjectAt (aDouble, nAddr);
        if (eErrCode == store_E_None)
        {
            // Truncate to 'nDouble', 'nSingle' pages.
            eErrCode = aDouble.truncate (nDouble, nSingle, rBIOS);
            if (eErrCode != store_E_None)
                return eErrCode;
        }
        else
        {
            if (eErrCode != store_E_InvalidChecksum)
                return eErrCode;
        }

        // Check for complete truncation.
        if ((nDouble + nSingle) == 0)
        {
            // Free double indirect page.
            eErrCode = rBIOS.free (nAddr);
            if (eErrCode != store_E_None)
                return eErrCode;
        }
    }
    return store_E_None;
}

/*
 * store_truncate_Impl (triple indirect page).
 */
static storeError store_truncate_Impl (
    sal_uInt32       nAddr,
    sal_uInt16       nTriple,
    sal_uInt16       nDouble,
    sal_uInt16       nSingle,
    OStorePageBIOS  &rBIOS)
{
    if (nAddr != STORE_PAGE_NULL)
    {
        // Load triple indirect page.
        OStoreIndirectionPageObject aTriple;
        storeError eErrCode = rBIOS.loadObjectAt (aTriple, nAddr);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Truncate to 'nTriple', 'nDouble', 'nSingle' pages.
        eErrCode = aTriple.truncate (nTriple, nDouble, nSingle, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Check for complete truncation.
        if ((nTriple + nDouble + nSingle) == 0)
        {
            // Free triple indirect page.
            eErrCode = rBIOS.free (nAddr);
            if (eErrCode != store_E_None)
                return eErrCode;
        }
    }
    return store_E_None;
}

/*
 * loadOrCreate.
 */
storeError OStoreIndirectionPageObject::loadOrCreate (
    sal_uInt32       nAddr,
    OStorePageBIOS & rBIOS)
{
    if (nAddr == STORE_PAGE_NULL)
    {
        storeError eErrCode = construct<page>(rBIOS.allocator());
        if (eErrCode != store_E_None)
            return eErrCode;

        eErrCode = rBIOS.allocate (*this);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Save location pending at caller.
        return store_E_Pending;
    }
    return rBIOS.loadObjectAt (*this, nAddr);
}

/*
 * guard.
 */
storeError OStoreIndirectionPageObject::guard (sal_uInt32 nAddr)
{
    return PageHolderObject< page >::guard (m_xPage, nAddr);
}

/*
 * verify.
 */
storeError OStoreIndirectionPageObject::verify (sal_uInt32 nAddr) const
{
    return PageHolderObject< page >::verify (m_xPage, nAddr);
}

/*
 * read (single indirect).
 */
storeError OStoreIndirectionPageObject::read (
    sal_uInt16             nSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page const & rPage = (*xImpl);

    // Check arguments.
    sal_uInt16 const nLimit = rPage.capacityCount();
    if (!(nSingle < nLimit))
        return store_E_InvalidAccess;

    // Obtain data page location.
    sal_uInt32 const nAddr = store::ntohl(rPage.m_pData[nSingle]);
    if (nAddr == STORE_PAGE_NULL)
        return store_E_NotExists;

    // Load data page and leave.
    return rBIOS.loadObjectAt (rData, nAddr);
}

/*
 * read (double indirect).
 */
storeError OStoreIndirectionPageObject::read (
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page const & rPage = (*xImpl);

    // Check arguments.
    sal_uInt16 const nLimit = rPage.capacityCount();
    if (!((nDouble < nLimit) && (nSingle < nLimit)))
        return store_E_InvalidAccess;

    // Check single indirect page location.
    sal_uInt32 const nAddr = store::ntohl(rPage.m_pData[nDouble]);
    if (nAddr == STORE_PAGE_NULL)
        return store_E_NotExists;

    // Load single indirect page.
    OStoreIndirectionPageObject aSingle;
    storeError eErrCode = rBIOS.loadObjectAt (aSingle, nAddr);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Read single indirect and leave.
    return aSingle.read (nSingle, rData, rBIOS);
}

/*
 * read (triple indirect).
 */
storeError OStoreIndirectionPageObject::read (
    sal_uInt16             nTriple,
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page const & rPage = (*xImpl);

    // Check arguments.
    sal_uInt16 const nLimit = rPage.capacityCount();
    if (!((nTriple < nLimit) && (nDouble < nLimit) && (nSingle < nLimit)))
        return store_E_InvalidAccess;

    // Check double indirect page location.
    sal_uInt32 const nAddr = store::ntohl(rPage.m_pData[nTriple]);
    if (nAddr == STORE_PAGE_NULL)
        return store_E_NotExists;

    // Load double indirect page.
    OStoreIndirectionPageObject aDouble;
    storeError eErrCode = rBIOS.loadObjectAt (aDouble, nAddr);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Read double indirect and leave.
    return aDouble.read (nDouble, nSingle, rData, rBIOS);
}

/*
 * write (single indirect).
 */
storeError OStoreIndirectionPageObject::write (
    sal_uInt16             nSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page & rPage = (*xImpl);

    // Check arguments.
    sal_uInt16 const nLimit = rPage.capacityCount();
    if (!(nSingle < nLimit))
        return store_E_InvalidAccess;

    // Obtain data page location.
    sal_uInt32 const nAddr = store::ntohl(rPage.m_pData[nSingle]);
    if (nAddr == STORE_PAGE_NULL)
    {
        // Allocate data page.
        storeError eErrCode = rBIOS.allocate (rData);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Store data page location.
        rPage.m_pData[nSingle] = store::htonl(rData.location());

        // Save this page.
        return rBIOS.saveObjectAt (*this, location());
    }
    else
    {
        // Save data page.
        return rBIOS.saveObjectAt (rData, nAddr);
    }
}

/*
 * write (double indirect).
 */
storeError OStoreIndirectionPageObject::write (
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page & rPage = (*xImpl);

    // Check arguments.
    sal_uInt16 const nLimit = rPage.capacityCount();
    if (!((nDouble < nLimit) && (nSingle < nLimit)))
        return store_E_InvalidAccess;

    // Load or create single indirect page.
    OStoreIndirectionPageObject aSingle;
    storeError eErrCode = aSingle.loadOrCreate (store::ntohl(rPage.m_pData[nDouble]), rBIOS);
    if (eErrCode != store_E_None)
    {
        if (eErrCode != store_E_Pending)
            return eErrCode;
        rPage.m_pData[nDouble] = store::htonl(aSingle.location());

        eErrCode = rBIOS.saveObjectAt (*this, location());
        if (eErrCode != store_E_None)
            return eErrCode;
    }

    // Write single indirect and leave.
    return aSingle.write (nSingle, rData, rBIOS);
}

/*
 * write (triple indirect).
 */
storeError OStoreIndirectionPageObject::write (
    sal_uInt16             nTriple,
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page & rPage = (*xImpl);

    // Check arguments.
    sal_uInt16 const nLimit = rPage.capacityCount();
    if (!((nTriple < nLimit) && (nDouble < nLimit) && (nSingle < nLimit)))
        return store_E_InvalidAccess;

    // Load or create double indirect page.
    OStoreIndirectionPageObject aDouble;
    storeError eErrCode = aDouble.loadOrCreate (store::ntohl(rPage.m_pData[nTriple]), rBIOS);
    if (eErrCode != store_E_None)
    {
        if (eErrCode != store_E_Pending)
            return eErrCode;
        rPage.m_pData[nTriple] = store::htonl(aDouble.location());

        eErrCode = rBIOS.saveObjectAt (*this, location());
        if (eErrCode != store_E_None)
            return eErrCode;
    }

    // Write double indirect and leave.
    return aDouble.write (nDouble, nSingle, rData, rBIOS);
}

/*
 * truncate (single indirect).
 */
storeError OStoreIndirectionPageObject::truncate (
    sal_uInt16       nSingle,
    OStorePageBIOS & rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page & rPage = (*xImpl);

    // Check arguments.
    sal_uInt16 const nLimit = rPage.capacityCount();
    if (!(nSingle < nLimit))
        return store_E_InvalidAccess;

    // Truncate.
    storeError eErrCode = store_E_None;
    for (sal_uInt16 i = nLimit; i > nSingle; i--)
    {
        // Obtain data page location.
        sal_uInt32 const nAddr = store::ntohl(rPage.m_pData[i - 1]);
        if (nAddr != STORE_PAGE_NULL)
        {
            // Free data page.
            eErrCode = rBIOS.free (nAddr);
            if (eErrCode != store_E_None)
                return eErrCode;

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

    // Check for modified page.
    if (dirty())
    {
        // Save this page.
        eErrCode = rBIOS.saveObjectAt (*this, location());
    }

    // Done.
    return eErrCode;
}

/*
 * truncate (double indirect).
 */
storeError OStoreIndirectionPageObject::truncate (
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    OStorePageBIOS        &rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page & rPage = (*xImpl);

    // Check arguments.
    sal_uInt16 const nLimit = rPage.capacityCount();
    if (!((nDouble < nLimit) && (nSingle < nLimit)))
        return store_E_InvalidAccess;

    // Truncate.
    storeError eErrCode = store_E_None;
    for (sal_uInt16 i = nLimit; i > nDouble + 1; i--)
    {
        // Truncate single indirect page to zero direct pages.
        eErrCode = store_truncate_Impl (store::ntohl(rPage.m_pData[i - 1]), 0, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

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

    // Truncate last single indirect page to 'nSingle' direct pages.
    eErrCode = store_truncate_Impl (store::ntohl(rPage.m_pData[nDouble]), nSingle, rBIOS);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Check for complete truncation.
    if (nSingle == 0)
    {
        // Clear pointer to last single indirect page.
        rPage.m_pData[nDouble] = STORE_PAGE_NULL;
        touch();
    }

    // Check for modified page.
    if (dirty())
    {
        // Save this page.
        eErrCode = rBIOS.saveObjectAt (*this, location());
    }

    // Done.
    return eErrCode;
}

/*
 * truncate (triple indirect).
 */
storeError OStoreIndirectionPageObject::truncate (
    sal_uInt16             nTriple,
    sal_uInt16             nDouble,
    sal_uInt16             nSingle,
    OStorePageBIOS        &rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page & rPage = (*xImpl);

    // Check arguments.
    sal_uInt16 const nLimit = rPage.capacityCount();
    if (!((nTriple < nLimit) && (nDouble < nLimit) && (nSingle < nLimit)))
        return store_E_InvalidAccess;

    // Truncate.
    storeError eErrCode = store_E_None;
    for (sal_uInt16 i = nLimit; i > nTriple + 1; i--)
    {
        // Truncate double indirect page to zero single indirect pages.
        eErrCode = store_truncate_Impl (store::ntohl(rPage.m_pData[i - 1]), 0, 0, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

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

    // Truncate last double indirect page to 'nDouble', 'nSingle' pages.
    eErrCode = store_truncate_Impl (store::ntohl(rPage.m_pData[nTriple]), nDouble, nSingle, rBIOS);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Check for complete truncation.
    if ((nDouble + nSingle) == 0)
    {
        // Clear pointer to last double indirect page.
        rPage.m_pData[nTriple] = STORE_PAGE_NULL;
        touch();
    }

    // Check for modified page.
    if (dirty())
    {
        // Save this page.
        eErrCode = rBIOS.saveObjectAt (*this, location());
    }

    // Done.
    return eErrCode;
}

/*========================================================================
 *
 * OStoreDirectoryPageObject implementation.
 *
 *======================================================================*/
/*
 * guard.
 */
storeError OStoreDirectoryPageObject::guard (sal_uInt32 nAddr)
{
    return PageHolderObject< page >::guard (m_xPage, nAddr);
}

/*
 * verify.
 */
storeError OStoreDirectoryPageObject::verify (sal_uInt32 nAddr) const
{
    return PageHolderObject< page >::verify (m_xPage, nAddr);
    // OLD: m_rPage.verifyVersion (STORE_MAGIC_DIRECTORYPAGE);
}

/*
 * scope (external data page; private).
 */
OStoreDirectoryPageData::ChunkScope
OStoreDirectoryPageObject::scope (
    sal_uInt32                       nPage,
    page::DataBlock::LinkDescriptor &rDescr) const
{
    page const & rPage = PAGE();
    OStoreDirectoryDataBlock const & rDataBlock = rPage.m_aDataBlock;

    sal_uInt32 index0, index1, index2, index3;

    // direct.
    sal_uInt32 nCount = rDataBlock.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 const nCapacity = indirect::capacityCount(rPage.m_aDescr);
    nCount = rDataBlock.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 = rDataBlock.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 = rDataBlock.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;
}

/*
 * read (external data page).
 */
storeError OStoreDirectoryPageObject::read (
    sal_uInt32             nPage,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS)
{
    // 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 const nAddr = directLink (aLink.m_nIndex0);
        if (nAddr == STORE_PAGE_NULL)
            return store_E_NotExists;

        eErrCode = rBIOS.loadObjectAt (rData, nAddr);
    }
    else if (eScope == page::SCOPE_SINGLE)
    {
        sal_uInt32 const nAddr = singleLink (aLink.m_nIndex1);
        if (nAddr == STORE_PAGE_NULL)
            return store_E_NotExists;

        OStoreIndirectionPageObject aSingle;
        eErrCode = rBIOS.loadObjectAt (aSingle, nAddr);
        if (eErrCode != store_E_None)
            return eErrCode;

        eErrCode = aSingle.read (aLink.m_nIndex0, rData, rBIOS);
    }
    else if (eScope == page::SCOPE_DOUBLE)
    {
        sal_uInt32 const nAddr = doubleLink (aLink.m_nIndex2);
        if (nAddr == STORE_PAGE_NULL)
            return store_E_NotExists;

        OStoreIndirectionPageObject aDouble;
        eErrCode = rBIOS.loadObjectAt (aDouble, nAddr);
        if (eErrCode != store_E_None)
            return eErrCode;

        eErrCode = aDouble.read (aLink.m_nIndex1, aLink.m_nIndex0, rData, rBIOS);
    }
    else if (eScope == page::SCOPE_TRIPLE)
    {
        sal_uInt32 const nAddr = tripleLink (aLink.m_nIndex3);
        if (nAddr == STORE_PAGE_NULL)
            return store_E_NotExists;

        OStoreIndirectionPageObject aTriple;
        eErrCode = rBIOS.loadObjectAt (aTriple, nAddr);
        if (eErrCode != store_E_None)
            return eErrCode;

        eErrCode = aTriple.read (aLink.m_nIndex2, aLink.m_nIndex1, aLink.m_nIndex0, rData, rBIOS);
    }
    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.
    return eErrCode;
}

/*
 * write (external data page).
 */
storeError OStoreDirectoryPageObject::write (
    sal_uInt32             nPage,
    OStoreDataPageObject  &rData,
    OStorePageBIOS        &rBIOS)
{
    // 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 const nAddr = directLink (aLink.m_nIndex0);
        if (nAddr == STORE_PAGE_NULL)
        {
            // Allocate data page.
            eErrCode = rBIOS.allocate (rData);
            if (eErrCode != store_E_None)
                return eErrCode;

            // Store data page location.
            directLink (aLink.m_nIndex0, rData.location());
        }
        else
        {
            // Save data page.
            eErrCode = rBIOS.saveObjectAt (rData, nAddr);
        }
    }
    else if (eScope == page::SCOPE_SINGLE)
    {
        OStoreIndirectionPageObject aSingle;
        eErrCode = aSingle.loadOrCreate (singleLink (aLink.m_nIndex1), rBIOS);
        if (eErrCode != store_E_None)
        {
            if (eErrCode != store_E_Pending)
                return eErrCode;
            singleLink (aLink.m_nIndex1, aSingle.location());
        }

        eErrCode = aSingle.write (aLink.m_nIndex0, rData, rBIOS);
    }
    else if (eScope == page::SCOPE_DOUBLE)
    {
        OStoreIndirectionPageObject aDouble;
        eErrCode = aDouble.loadOrCreate (doubleLink (aLink.m_nIndex2), rBIOS);
        if (eErrCode != store_E_None)
        {
            if (eErrCode != store_E_Pending)
                return eErrCode;
            doubleLink (aLink.m_nIndex2, aDouble.location());
        }

        eErrCode = aDouble.write (aLink.m_nIndex1, aLink.m_nIndex0, rData, rBIOS);
    }
    else if (eScope == page::SCOPE_TRIPLE)
    {
        OStoreIndirectionPageObject aTriple;
        eErrCode = aTriple.loadOrCreate (tripleLink (aLink.m_nIndex3), rBIOS);
        if (eErrCode != store_E_None)
        {
            if (eErrCode != store_E_Pending)
                return eErrCode;
            tripleLink (aLink.m_nIndex3, aTriple.location());
        }

        eErrCode = aTriple.write (aLink.m_nIndex2, aLink.m_nIndex1, aLink.m_nIndex0, rData, rBIOS);
    }
    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.
    return eErrCode;
}

/*
 * truncate (external data page).
 */
storeError OStoreDirectoryPageObject::truncate (
    sal_uInt32             nPage,
    OStorePageBIOS        &rBIOS)
{
    // 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, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Truncate all double indirect pages.
        eErrCode = truncate (page::SCOPE_DOUBLE, 0, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Truncate all single indirect pages.
        eErrCode = truncate (page::SCOPE_SINGLE, 0, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

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

        // Truncate all double indirect pages.
        eErrCode = truncate (page::SCOPE_DOUBLE, 0, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Truncate single indirect pages, downto 'aLink.m_nIndex1'.
        eErrCode = truncate (eScope, aLink.m_nIndex1 + 1, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Truncate last single indirect page to ... pages.
        eErrCode = store_truncate_Impl (singleLink (aLink.m_nIndex1), aLink.m_nIndex0, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Check for complete truncation.
        if (aLink.m_nIndex0 == 0)
        {
            // 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, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Truncate double indirect pages, downto 'aLink.m_nIndex2'.
        eErrCode = truncate (eScope, aLink.m_nIndex2 + 1, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Truncate last double indirect page to ... pages.
        eErrCode = store_truncate_Impl (
            doubleLink (aLink.m_nIndex2), aLink.m_nIndex1, aLink.m_nIndex0, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Check for complete truncation.
        if ((aLink.m_nIndex1 + aLink.m_nIndex0) == 0)
        {
            // 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, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Truncate last triple indirect page to ... pages.
        eErrCode = store_truncate_Impl (
            tripleLink (aLink.m_nIndex3), aLink.m_nIndex2, aLink.m_nIndex1, aLink.m_nIndex0, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Check for complete truncation.
        if ((aLink.m_nIndex2 + aLink.m_nIndex1 + aLink.m_nIndex0) == 0)
        {
            // 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.
    return eErrCode;
}

/*
 * truncate (external data page scope; private).
 */
storeError OStoreDirectoryPageObject::truncate (
    page::ChunkScope       eScope,
    sal_uInt16             nRemain,
    OStorePageBIOS        &rBIOS)
{
    OStoreDirectoryDataBlock const & rDataBlock = PAGE().m_aDataBlock;

    // Enter.
    storeError eErrCode = store_E_None;
    if (eScope == page::SCOPE_DIRECT)
    {
        // Truncate direct data pages.
        sal_uInt16 i, n = rDataBlock.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.
            eErrCode = rBIOS.free (nAddr);
            if (eErrCode != store_E_None)
                break;

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

        // Done.
        return eErrCode;
    }

    if (eScope == page::SCOPE_SINGLE)
    {
        // Truncate single indirect pages.
        sal_uInt16 i, n = rDataBlock.singleCount();
        for (i = n; i > nRemain; i--)
        {
            // Truncate single indirect page to zero data pages.
            eErrCode = store_truncate_Impl (singleLink (i - 1), 0, rBIOS);
            if (eErrCode != store_E_None)
                break;

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

        // Done.
        return eErrCode;
    }

    if (eScope == page::SCOPE_DOUBLE)
    {
        // Truncate double indirect pages.
        sal_uInt16 i, n = rDataBlock.doubleCount();
        for (i = n; i > nRemain; i--)
        {
            // Truncate double indirect page to zero single indirect pages.
            eErrCode = store_truncate_Impl (doubleLink (i - 1), 0, 0, rBIOS);
            if (eErrCode != store_E_None)
                break;

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

        // Done.
        return eErrCode;
    }

    if (eScope == page::SCOPE_TRIPLE)
    {
        // Truncate triple indirect pages.
        sal_uInt16 i, n = rDataBlock.tripleCount();
        for (i = n; i > nRemain; i--)
        {
            // Truncate to zero double indirect pages.
            eErrCode = store_truncate_Impl (tripleLink (i - 1), 0, 0, 0, rBIOS);
            if (eErrCode != store_E_None)
                break;

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

        // Done.
        return eErrCode;
    }

    // Invalid scope.
    return store_E_InvalidAccess;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
