/**************************************************************
 * 
 * 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 "storbios.hxx"

#include "sal/types.h"
#include "sal/macros.h"

#include "rtl/alloc.h"
#include "rtl/ref.hxx"

#include "osl/diagnose.h"
#include "osl/mutex.hxx"

#include "store/types.h"
#include "object.hxx"
#include "lockbyte.hxx"
#include "storcach.hxx"

using namespace store;

/*========================================================================
 *
 * OStoreSuperBlock.
 *
 *======================================================================*/
#define STORE_MAGIC_SUPERBLOCK sal_uInt32(0x484D5343)

struct OStoreSuperBlock
{
	typedef OStorePageGuard      G;
	typedef OStorePageDescriptor D;
	typedef OStorePageLink       L;

	/** Representation.
	 */
	G          m_aGuard;
	D          m_aDescr;
	sal_uInt32 m_nMarked;
	L          m_aMarked;
	sal_uInt32 m_nUnused;
	L          m_aUnused;

	/** theSize.
	 */
	static const size_t theSize = sizeof(G) + sizeof(D) + 2 * (sizeof(L) + sizeof(sal_uInt32));

	/** Construction.
	 */
	explicit OStoreSuperBlock (sal_uInt16 nPageSize)
		: m_aGuard  (STORE_MAGIC_SUPERBLOCK),
          m_aDescr  (nPageSize, nPageSize, STORE_MINIMUM_PAGESIZE),
		  m_nMarked (store::htonl(0)),
		  m_aMarked (0),
		  m_nUnused (store::htonl(0)),
		  m_aUnused (0)
	{}

	OStoreSuperBlock (const OStoreSuperBlock & rhs)
		: m_aGuard  (rhs.m_aGuard),
		  m_aDescr  (rhs.m_aDescr),
		  m_nMarked (rhs.m_nMarked),
		  m_aMarked (rhs.m_aMarked),
		  m_nUnused (rhs.m_nUnused),
		  m_aUnused (rhs.m_aUnused)
	{}

	OStoreSuperBlock& operator= (const OStoreSuperBlock & rhs)
	{
		m_aGuard  = rhs.m_aGuard;
		m_aDescr  = rhs.m_aDescr;
		m_nMarked = rhs.m_nMarked;
		m_aMarked = rhs.m_aMarked;
		m_nUnused = rhs.m_nUnused;
		m_aUnused = rhs.m_aUnused;
		return *this;
	}

	/** Comparison.
	 */
	sal_Bool operator== (const OStoreSuperBlock & rhs) const
	{
		return ((m_aGuard  == rhs.m_aGuard ) &&
				(m_aDescr  == rhs.m_aDescr ) &&
				(m_nMarked == rhs.m_nMarked) &&
				(m_aMarked == rhs.m_aMarked) &&
				(m_nUnused == rhs.m_nUnused) &&
				(m_aUnused == rhs.m_aUnused)    );
	}

	/** unused(Count|Head|Insert|Remove|Reset).
	 */
	sal_uInt32 unusedCount (void) const
	{
		return store::ntohl(m_nUnused);
	}
	const L& unusedHead (void) const
	{
		return m_aUnused;
	}
	void unusedInsert (const L& rLink)
	{
        sal_uInt32 nUnused = unusedCount();
        m_nUnused = store::htonl(nUnused + 1);
		m_aUnused = rLink;
	}
	void unusedRemove (const L& rLink)
	{
        sal_uInt32 nUnused = unusedCount();
        m_nUnused = store::htonl(nUnused - 1);
		m_aUnused = rLink;
	}
	void unusedReset (void)
	{
		m_nUnused = store::htonl(0);
		m_aUnused = L(0);
	}

	/** guard (external representation).
	 */
	void guard()
	{
		sal_uInt32 nCRC32 = 0;
		nCRC32 = rtl_crc32 (nCRC32, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
		nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, theSize - sizeof(G));
		m_aGuard.m_nCRC32 = store::htonl(nCRC32);
	}

	/** verify (external representation).
	 */
	storeError verify() const
	{
		sal_uInt32 nMagic = store::ntohl(m_aGuard.m_nMagic);
		if (nMagic != STORE_MAGIC_SUPERBLOCK)
			return store_E_WrongFormat;

		sal_uInt32 nCRC32 = 0;
		nCRC32 = rtl_crc32 (nCRC32, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
		nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, theSize - sizeof(G));
		if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
			return store_E_InvalidChecksum;
		else
			return store_E_None;
	}
};

/*========================================================================
 *
 * SuperBlockPage interface.
 *
 *======================================================================*/
namespace store
{

struct SuperBlockPage
{
	typedef OStoreSuperBlock SuperBlock;

	/** Representation.
	 */
	SuperBlock m_aSuperOne;
	SuperBlock m_aSuperTwo;

	/** theSize.
	 */
	static const size_t     theSize     = 2 * SuperBlock::theSize;
    static const sal_uInt16 thePageSize = theSize;
	STORE_STATIC_ASSERT(STORE_MINIMUM_PAGESIZE >= thePageSize);

	/** Allocation.
	 */
	static void * operator new (size_t n) SAL_THROW(())
	{
		return rtl_allocateMemory (sal::static_int_cast<sal_Size>(n));
	}
	static void operator delete (void * p, size_t) SAL_THROW(())
	{
		rtl_freeMemory (p);
	}

    static void * operator new (size_t, sal_uInt16 nPageSize) SAL_THROW(())
    {
        return rtl_allocateZeroMemory (sal::static_int_cast<sal_Size>(nPageSize));
    }
    static void operator delete (void * p, sal_uInt16) SAL_THROW(())
    {
        rtl_freeMemory (p);
    }

	/** Construction.
	 */
	explicit SuperBlockPage (sal_uInt16 nPageSize = thePageSize)
        : m_aSuperOne(nPageSize),
          m_aSuperTwo(nPageSize)
	{}

	/** save.
	 */
	storeError save (OStorePageBIOS & rBIOS, sal_uInt32 nSize = theSize)
	{
        m_aSuperOne.guard();
        m_aSuperTwo = m_aSuperOne;
		return rBIOS.write (0, this, nSize);
	}

    /** Page allocation.
     */
    storeError unusedHead (
        OStorePageBIOS & rBIOS,
        PageData &       rPageHead);

    storeError unusedPop (
        OStorePageBIOS & rBIOS,
        PageData const & rPageHead);

    storeError unusedPush (
        OStorePageBIOS & rBIOS,
        sal_uInt32       nAddr);

	/** verify (with repair).
	 */
	storeError verify (OStorePageBIOS & rBIOS);
};

} // namespace store

/*========================================================================
 *
 * SuperBlockPage implementation.
 *
 *======================================================================*/
/*
 * unusedHead(): get freelist head (alloc page, step 1).
 */
storeError SuperBlockPage::unusedHead (OStorePageBIOS & rBIOS, PageData & rPageHead)
{
    storeError eErrCode = verify (rBIOS);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Check freelist head.
    OStorePageLink const aListHead (m_aSuperOne.unusedHead());
    if (aListHead.location() == 0)
    {
        // Freelist empty, see SuperBlock::ctor().
        rPageHead.location (STORE_PAGE_NULL);
        return store_E_None;
    }

    // Load PageHead.
    eErrCode = rBIOS.read (aListHead.location(), &rPageHead, PageData::theSize);
    if (eErrCode != store_E_None)
        return eErrCode;

    eErrCode = rPageHead.verify (aListHead.location());
    if (eErrCode != store_E_None)
        return eErrCode;

    // Verify page is unused.
    sal_uInt32 const nAddr = rPageHead.m_aUnused.location();
    OSL_POSTCOND(nAddr != STORE_PAGE_NULL, "store::SuperBlock::unusedHead(): page not free");
    if (nAddr == STORE_PAGE_NULL)
    {
        // Page in use.
        rPageHead.location (STORE_PAGE_NULL);

        // Recovery: Reset freelist to empty.
        m_aSuperOne.unusedReset();
        eErrCode = save (rBIOS);
    }
    return eErrCode;
}

/*
 * unusedPop(): pop freelist head (alloc page, step 2).
 */
storeError SuperBlockPage::unusedPop (OStorePageBIOS & rBIOS, PageData const & rPageHead)
{
    sal_uInt32 const nAddr = rPageHead.m_aUnused.location();
    OSL_PRECOND(nAddr != STORE_PAGE_NULL, "store::SuperBlock::unusedPop(): page not free");
    if (nAddr == STORE_PAGE_NULL)
        return store_E_CantSeek;

    // Pop from FreeList.
    OStorePageLink const aListHead (nAddr);
    m_aSuperOne.unusedRemove (aListHead);
    return save (rBIOS);
}

/*
 * unusedPush(): push new freelist head.
 */
storeError SuperBlockPage::unusedPush (OStorePageBIOS & rBIOS, sal_uInt32 nAddr)
{
    storeError eErrCode = verify (rBIOS);
    if (eErrCode != store_E_None)
        return eErrCode;

    PageData aPageHead;
    eErrCode = rBIOS.read (nAddr, &aPageHead, PageData::theSize);
    if (eErrCode != store_E_None)
        return eErrCode;

    eErrCode = aPageHead.verify (nAddr);
    if (eErrCode != store_E_None)
        return eErrCode;

    aPageHead.m_aUnused = m_aSuperOne.unusedHead();
    aPageHead.guard (nAddr);

    eErrCode = rBIOS.write (nAddr, &aPageHead, PageData::theSize);
    if (eErrCode != store_E_None)
        return eErrCode;

    OStorePageLink const aListHead (nAddr);
    m_aSuperOne.unusedInsert(aListHead);
    return save (rBIOS);
}

/*
 * verify (with repair).
 */
storeError SuperBlockPage::verify (OStorePageBIOS & rBIOS)
{
	// Verify 1st copy.
	storeError eErrCode = m_aSuperOne.verify();
	if (eErrCode == store_E_None)
	{
		// Ok. Verify 2nd copy.
		eErrCode = m_aSuperTwo.verify();
		if (eErrCode == store_E_None)
		{
			// Ok. Ensure identical copies (1st copy wins).
			if (!(m_aSuperOne == m_aSuperTwo))
			{
				// Different. Replace 2nd copy with 1st copy.
				m_aSuperTwo = m_aSuperOne;

				// Write back.
				if (rBIOS.isWriteable())
					eErrCode = rBIOS.write (0, this, theSize);
				else
					eErrCode = store_E_None;
			}
		}
		else
		{
			// Failure. Replace 2nd copy with 1st copy.
			m_aSuperTwo = m_aSuperOne;

			// Write back.
			if (rBIOS.isWriteable())
				eErrCode = rBIOS.write (0, this, theSize);
			else
				eErrCode = store_E_None;
		}
	}
	else
	{
		// Failure. Verify 2nd copy.
		eErrCode = m_aSuperTwo.verify();
		if (eErrCode == store_E_None)
		{
			// Ok. Replace 1st copy with 2nd copy.
			m_aSuperOne = m_aSuperTwo;

			// Write back.
			if (rBIOS.isWriteable())
				eErrCode = rBIOS.write (0, this, theSize);
			else
				eErrCode = store_E_None;
		}
		else
		{
			// Double Failure.
			OSL_TRACE("OStoreSuperBlockPage::verify(): double failure.\n");
		}
	}

	// Done.
	return eErrCode;
}

/*========================================================================
 *
 * OStorePageBIOS::Ace implementation.
 *
 *======================================================================*/
OStorePageBIOS::Ace::Ace()
  : m_next (this), m_prev (this), m_addr (STORE_PAGE_NULL), m_used (0)
{}

OStorePageBIOS::Ace::~Ace()
{
  m_next->m_prev = m_prev, m_prev->m_next = m_next;
}

int
SAL_CALL OStorePageBIOS::Ace::constructor (void * obj, void * /* arg */)
{
  Ace * ace = static_cast<Ace*>(obj);
  ace->m_next = ace->m_prev = ace;
  return 1;
}

OStorePageBIOS::Ace *
OStorePageBIOS::Ace::find (OStorePageBIOS::Ace * head, sal_uInt32 addr)
{
  OStorePageBIOS::Ace * entry;
  for (entry = head->m_next; entry != head; entry = entry->m_next)
  {
    if (entry->m_addr >= addr)
      return entry;
  }
  return head;
}

void
OStorePageBIOS::Ace::insert (OStorePageBIOS::Ace * head, OStorePageBIOS::Ace * entry)
{
  // insert entry at queue tail (before head).
  entry->m_next = head;
  entry->m_prev = head->m_prev;
  head->m_prev = entry;
  entry->m_prev->m_next = entry;
}

/*========================================================================
 *
 * OStorePageBIOS::AceCache interface.
 *
 *======================================================================*/
namespace store
{

class OStorePageBIOS::AceCache
{
  rtl_cache_type * m_ace_cache;

public:
  static AceCache & get();

  OStorePageBIOS::Ace *
  create (sal_uInt32 addr, sal_uInt32 used = 1);

  void
  destroy (OStorePageBIOS::Ace * ace);

protected:
  AceCache();
  ~AceCache();
};

} // namespace store

/*========================================================================
 *
 * OStorePageBIOS::AceCache implementation.
 *
 *======================================================================*/
extern "C"  typedef  int (SAL_CALL * ace_constructor_type)(void*,void*);

OStorePageBIOS::AceCache &
OStorePageBIOS::AceCache::get()
{
  static AceCache g_ace_cache;
  return g_ace_cache;
}

OStorePageBIOS::AceCache::AceCache()
{
  m_ace_cache = rtl_cache_create (
    "store_ace_cache",
    sizeof (OStorePageBIOS::Ace),
    0, // objalign
   reinterpret_cast<ace_constructor_type>( OStorePageBIOS::Ace::constructor),
    0, // destructor,
    0, // reclaim,
    0, // userarg,
    0, // default source,
    0  // flags
    );
}

OStorePageBIOS::AceCache::~AceCache()
{
  rtl_cache_destroy (m_ace_cache), m_ace_cache = 0;
}

OStorePageBIOS::Ace *
OStorePageBIOS::AceCache::create (sal_uInt32 addr, sal_uInt32 used)
{
  Ace * ace = static_cast<Ace*>(rtl_cache_alloc (m_ace_cache));
  if (ace != 0)
  {
    // verify invariant state.
    OSL_ASSERT((ace->m_next == ace) && (ace->m_prev == ace));

    // initialize.
    ace->m_addr = addr;
    ace->m_used = used;
  }
  return ace;
}

void
OStorePageBIOS::AceCache::destroy (OStorePageBIOS::Ace * ace)
{
  if (ace != 0)
  {
    // remove from queue (if any).
    ace->m_next->m_prev = ace->m_prev, ace->m_prev->m_next = ace->m_next;

    // restore invariant state.
    ace->m_next = ace->m_prev = ace;

    // return to cache.
    rtl_cache_free (m_ace_cache, ace);
  }
}

/*========================================================================
 *
 * OStorePageBIOS implementation.
 *
 *======================================================================*/
/*
 * OStorePageBIOS.
 */
OStorePageBIOS::OStorePageBIOS (void)
	: m_xLockBytes (NULL),
	  m_pSuper     (NULL),
	  m_bWriteable (false)
{
}

/*
 * ~OStorePageBIOS.
 */
OStorePageBIOS::~OStorePageBIOS (void)
{
	cleanup_Impl();
}

/*
 * initialize.
 * Precond: none.
 */
storeError OStorePageBIOS::initialize (
	ILockBytes *    pLockBytes,
	storeAccessMode eAccessMode,
	sal_uInt16 &    rnPageSize)
{
	// Acquire exclusive access.
    osl::MutexGuard aGuard (m_aMutex);

    // Initialize.
    storeError eErrCode = initialize_Impl (pLockBytes, eAccessMode, rnPageSize);
    if (eErrCode != store_E_None)
    {
        // Cleanup.
        cleanup_Impl();
    }
    return eErrCode;
}

/*
 * initialize_Impl.
 * Internal: Precond: exclusive access.
 */
storeError OStorePageBIOS::initialize_Impl (
	ILockBytes *    pLockBytes,
	storeAccessMode eAccessMode,
	sal_uInt16 &    rnPageSize)
{
    // Cleanup.
    cleanup_Impl();

    // Initialize.
    m_xLockBytes = pLockBytes;
    if (!m_xLockBytes.is())
        return store_E_InvalidParameter;
    m_bWriteable = (eAccessMode != store_AccessReadOnly);

    // Check access mode.
    storeError eErrCode = store_E_None;
    if (eAccessMode != store_AccessCreate)
    {
        // Load SuperBlock page.
        if ((m_pSuper = new SuperBlockPage()) == 0)
            return store_E_OutOfMemory;

        eErrCode = read (0, m_pSuper, SuperBlockPage::theSize);
        if (eErrCode == store_E_None)
        {
            // Verify SuperBlock page (with repair).
            eErrCode = m_pSuper->verify (*this);
        }
    }
    else
    {
        // Truncate to zero length.
        eErrCode = m_xLockBytes->setSize(0);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Mark as not existing.
        eErrCode = store_E_NotExists;
    }

    if (eErrCode != store_E_None)
    {
        // Check reason.
        if (eErrCode != store_E_NotExists)
            return eErrCode;

        // Check mode.
        if (eAccessMode == store_AccessReadOnly)
            return store_E_NotExists;
        if (eAccessMode == store_AccessReadWrite)
            return store_E_NotExists;

        // Check PageSize.
        if ((STORE_MINIMUM_PAGESIZE > rnPageSize) || (rnPageSize > STORE_MAXIMUM_PAGESIZE))
            return store_E_InvalidParameter;
        rnPageSize = ((rnPageSize + STORE_MINIMUM_PAGESIZE - 1) & ~(STORE_MINIMUM_PAGESIZE - 1));

        // Create initial page (w/ SuperBlock).
        if ((m_pSuper = new(rnPageSize) SuperBlockPage(rnPageSize)) == 0)
            return store_E_OutOfMemory;
        eErrCode = m_pSuper->save (*this, rnPageSize);
    }
    if (eErrCode == store_E_None)
    {
        // Obtain page size.
        rnPageSize = store::ntohs(m_pSuper->m_aSuperOne.m_aDescr.m_nSize);

        // Create page allocator.
        eErrCode = m_xLockBytes->initialize (m_xAllocator, rnPageSize);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Create page cache.
        eErrCode = PageCache_createInstance (m_xCache, rnPageSize);
    }
	return eErrCode;
}

/*
 * cleanup_Impl.
 * Internal: Precond: exclusive access.
 */
void OStorePageBIOS::cleanup_Impl()
{
	// Check referer count.
	if (m_ace_head.m_used > 0)
	{
		// Report remaining referer count.
		OSL_TRACE("store::PageBIOS::cleanup_Impl(): referer count: %d\n", m_ace_head.m_used);
        for (Ace * ace = m_ace_head.m_next; ace != &m_ace_head; ace = m_ace_head.m_next)
        {
            m_ace_head.m_used -= ace->m_used;
            AceCache::get().destroy (ace);
        }
        OSL_ENSURE(m_ace_head.m_used == 0, "store::PageBIOS::cleanup_Impl(): logic error");
	}

    // Release SuperBlock page.
    delete m_pSuper, m_pSuper = 0;

    // Release PageCache.
    m_xCache.clear();

    // Release PageAllocator.
    m_xAllocator.clear();

    // Release LockBytes.
    m_xLockBytes.clear();
}

/*
 * read.
 * Low Level: Precond: initialized, exclusive access.
 */
storeError OStorePageBIOS::read (
	sal_uInt32 nAddr, void *pData, sal_uInt32 nSize)
{
	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;

	// Read Data.
	return m_xLockBytes->readAt (nAddr, pData, nSize);
}

/*
 * write.
 * Low Level: Precond: initialized, writeable, exclusive access.
 */
storeError OStorePageBIOS::write (
	sal_uInt32 nAddr, const void *pData, sal_uInt32 nSize)
{
	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;
	if (!m_bWriteable)
		return store_E_AccessViolation;

	// Write Data.
	return m_xLockBytes->writeAt (nAddr, pData, nSize);
}

/*
 * acquirePage.
 * Precond: initialized.
 */
storeError OStorePageBIOS::acquirePage (
	const OStorePageDescriptor& rDescr, storeAccessMode eMode)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;

	// Check access mode.
	if (!(m_bWriteable || (eMode == store_AccessReadOnly)))
		return store_E_AccessViolation;

	// Find access control list entry.
	Ace * ace = Ace::find (&m_ace_head, rDescr.m_nAddr);
	if (ace->m_addr == rDescr.m_nAddr)
	{
	  // Acquire existing entry (with ShareDenyWrite).
	  if (eMode == store_AccessReadOnly)
	    ace->m_used += 1;
	  else
	    return store_E_AccessViolation;
	}
	else
	{
	  // Insert new entry.
	  Ace * entry = AceCache::get().create (rDescr.m_nAddr, 1);
	  if (!entry)
	    return store_E_OutOfMemory;
	  Ace::insert (ace, entry);
	}

	// Increment total referer count and finish.
	m_ace_head.m_used += 1;
	return store_E_None;
}

/*
 * releasePage.
 * Precond: initialized.
 */
storeError OStorePageBIOS::releasePage (
	const OStorePageDescriptor& rDescr, storeAccessMode /* eMode */)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;

	// Find access control list entry.
	Ace * ace = Ace::find (&m_ace_head, rDescr.m_nAddr);
	if (ace->m_addr != rDescr.m_nAddr)
	  return store_E_NotExists;

	// Release existing entry.
	if (ace->m_used > 1)
	  ace->m_used -= 1;
	else
	  AceCache::get().destroy (ace);

	// Decrement total referer count and finish.
	m_ace_head.m_used -= 1;
	return store_E_None;
}

/*
 * getRefererCount.
 * Precond: none.
 */
sal_uInt32 OStorePageBIOS::getRefererCount (void)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Obtain total referer count.
	return m_ace_head.m_used;
}

/*
 * allocate.
 * Precond: initialized, writeable.
 */
storeError OStorePageBIOS::allocate (
	OStorePageObject& rPage, Allocation eAlloc)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;
	if (!m_bWriteable)
		return store_E_AccessViolation;

	// Check allocation type.
	storeError eErrCode = store_E_None;
	if (eAlloc != ALLOCATE_EOF)
	{
        // Try freelist head.
        PageData aPageHead;
        eErrCode = m_pSuper->unusedHead (*this, aPageHead);
        if (eErrCode != store_E_None)
            return eErrCode;

        sal_uInt32 const nAddr = aPageHead.location();
        if (nAddr != STORE_PAGE_NULL)
        {
            // Save page.
            eErrCode = saveObjectAt_Impl (rPage, nAddr);
			if (eErrCode != store_E_None)
				return eErrCode;

			// Pop freelist head and finish.
            return m_pSuper->unusedPop (*this, aPageHead);
        }
	}

	// Allocate from EOF. Determine current size.
	sal_uInt32 nSize = STORE_PAGE_NULL;
	eErrCode = m_xLockBytes->getSize (nSize);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Save page at current EOF.
    return saveObjectAt_Impl (rPage, nSize);
}

/*
 * free.
 * Precond: initialized, writeable.
 */
storeError OStorePageBIOS::free (sal_uInt32 nAddr)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;
	if (!m_bWriteable)
		return store_E_AccessViolation;

    // Invalidate cache.
    (void) m_xCache->removePageAt (nAddr);

	// Push onto freelist.
    return m_pSuper->unusedPush (*this, nAddr);
}

/*
 * loadObjectAt.
 * Precond: initialized, readable.
 */
storeError OStorePageBIOS::loadObjectAt (OStorePageObject & rPage, sal_uInt32 nAddr)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;

    return loadObjectAt_Impl (rPage, nAddr);
}

/*
 * loadObjectAt_Impl.
 * Internal: Precond: initialized, readable, exclusive access.
 */
storeError OStorePageBIOS::loadObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr)
{
    storeError eErrCode = m_xCache->lookupPageAt (rPage.get(), nAddr);
    if (eErrCode != store_E_NotExists)
        return eErrCode;

    // Read page.
    eErrCode = m_xLockBytes->readPageAt (rPage.get(), nAddr);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Verify page.
    eErrCode = rPage.verify (nAddr);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Mark page as clean.
    rPage.clean();

    // Cache page.
    return m_xCache->insertPageAt (rPage.get(), nAddr);
}

/*
 * saveObjectAt.
 * Precond: initialized, writeable.
 */
storeError OStorePageBIOS::saveObjectAt (OStorePageObject & rPage, sal_uInt32 nAddr)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;
	if (!m_bWriteable)
		return store_E_AccessViolation;

	// Save Page.
	return saveObjectAt_Impl (rPage, nAddr);
}

/*
 * saveObjectAt_Impl.
 * Internal: Precond: initialized, writeable, exclusive access.
 */
storeError OStorePageBIOS::saveObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr)
{
    // Guard page (incl. set location).
    storeError eErrCode = rPage.guard (nAddr);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Write page.
    eErrCode = m_xLockBytes->writePageAt(rPage.get(), nAddr);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Mark page as clean.
    rPage.clean();

    // Cache page.
    return m_xCache->updatePageAt (rPage.get(), nAddr);
}

/*
 * close.
 * Precond: none.
 */
storeError OStorePageBIOS::close()
{
	// Acquire exclusive access.
    osl::MutexGuard aGuard (m_aMutex);

    // Cleanup.
    cleanup_Impl();

	// Done.
    return store_E_None;
}

/*
 * flush.
 * Precond: initialized.
 */
storeError OStorePageBIOS::flush (void)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;

	// Flush LockBytes and finish.
	return m_xLockBytes->flush();
}

/*
 * size.
 * Precond: initialized.
 */
storeError OStorePageBIOS::size (sal_uInt32 &rnSize)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Initialize [out] param.
	rnSize = 0;

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;

	// Obtain LockBytes size.
	return m_xLockBytes->getSize (rnSize);
}

/*
 * scanBegin.
 * Precond: initialized.
 */
storeError OStorePageBIOS::scanBegin (
	ScanContext &rCtx, sal_uInt32 nMagic)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Initialize [out] param.
	rCtx.m_aDescr = OStorePageDescriptor(0, 0, 0);
	rCtx.m_nSize  = 0;
	rCtx.m_nMagic = nMagic;

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;

	// Check SuperBlock page.
	storeError eErrCode = m_pSuper->verify (*this);
	if (eErrCode != store_E_None)
	{
		// Damaged. Determine page size (NYI).
		OSL_TRACE ("OStorePageBIOS::scanBegin(): damaged.\n");
		return eErrCode;
	}

	// Setup Context descriptor.
	rCtx.m_aDescr = m_pSuper->m_aSuperOne.m_aDescr;
	rCtx.m_aDescr.m_nSize = store::ntohs(rCtx.m_aDescr.m_nSize);
	rCtx.m_aDescr.m_nAddr = rCtx.m_aDescr.m_nSize;

	// Setup Context size.
	eErrCode = size (rCtx.m_nSize);
	if (eErrCode != store_E_None)
		rCtx.m_nSize = ((sal_uInt32)(~0));

	// Done.
	return store_E_None;
}

/*
 * scanNext.
 * Precond: initialized.
 */
storeError OStorePageBIOS::scanNext (
	ScanContext &rCtx, OStorePageObject &rPage)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard (m_aMutex);

	// Check precond.
	if (!m_xLockBytes.is())
		return store_E_InvalidAccess;

	// Setup PageHead.
	PageData aPageHead;

	// Check context.
	while (rCtx.isValid())
	{
		// Assign next location.
		sal_uInt32 nAddr = rCtx.m_aDescr.m_nAddr;
		rCtx.m_aDescr.m_nAddr += rCtx.m_aDescr.m_nSize;

        // Read PageHead.
        storeError eErrCode = read (nAddr, &aPageHead, PageData::theSize);
        if (eErrCode != store_E_None)
			continue;

        // Verify PageHead.
        eErrCode = aPageHead.verify (nAddr);
        if (eErrCode != store_E_None)
			continue;

		// Check PageHead Magic number.
		if (aPageHead.m_aGuard.m_nMagic != rCtx.m_nMagic)
			continue;

		// Check PageHead Unused link.
		if (aPageHead.m_aUnused.m_nAddr != STORE_PAGE_NULL)
			continue;

		// Load page.
		eErrCode = loadObjectAt_Impl (rPage, nAddr);
		if (eErrCode != store_E_None)
			continue;

		// Deliver page.
		return store_E_None;
	}

	// Done.
	return store_E_CantSeek;
}
