summaryrefslogtreecommitdiffstats
path: root/store/source
diff options
context:
space:
mode:
Diffstat (limited to 'store/source')
-rw-r--r--store/source/lockbyte.cxx886
-rw-r--r--store/source/lockbyte.hxx154
-rw-r--r--store/source/object.cxx32
-rw-r--r--store/source/object.hxx54
-rw-r--r--store/source/storbase.cxx124
-rw-r--r--store/source/storbase.hxx608
-rw-r--r--store/source/storbios.cxx890
-rw-r--r--store/source/storbios.hxx170
-rw-r--r--store/source/storcach.cxx414
-rw-r--r--store/source/storcach.hxx99
-rw-r--r--store/source/stordata.cxx1043
-rw-r--r--store/source/stordata.hxx748
-rw-r--r--store/source/stordir.cxx184
-rw-r--r--store/source/stordir.hxx104
-rw-r--r--store/source/store.cxx371
-rw-r--r--store/source/storlckb.cxx387
-rw-r--r--store/source/storlckb.hxx131
-rw-r--r--store/source/storpage.cxx494
-rw-r--r--store/source/storpage.hxx156
-rw-r--r--store/source/stortree.cxx507
-rw-r--r--store/source/stortree.hxx248
21 files changed, 7804 insertions, 0 deletions
diff --git a/store/source/lockbyte.cxx b/store/source/lockbyte.cxx
new file mode 100644
index 0000000000..87aefdbe41
--- /dev/null
+++ b/store/source/lockbyte.cxx
@@ -0,0 +1,886 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "lockbyte.hxx"
+
+#include <sal/types.h>
+#include <osl/diagnose.h>
+#include <osl/file.h>
+#include <osl/process.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include "object.hxx"
+#include "storbase.hxx"
+
+#include <memory>
+#include <string.h>
+
+using namespace store;
+
+storeError ILockBytes::initialize (rtl::Reference< PageData::Allocator > & rxAllocator, sal_uInt16 nPageSize)
+{
+ OSL_PRECOND((STORE_MINIMUM_PAGESIZE <= nPageSize) && (nPageSize <= STORE_MAXIMUM_PAGESIZE), "invalid PageSize");
+ return initialize_Impl (rxAllocator, nPageSize);
+}
+
+storeError ILockBytes::readPageAt (std::shared_ptr<PageData> & rPage, sal_uInt32 nOffset)
+{
+ OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::ILockBytes::readPageAt(): invalid Offset");
+ if (nOffset == STORE_PAGE_NULL)
+ return store_E_CantSeek;
+
+ return readPageAt_Impl (rPage, nOffset);
+}
+
+storeError ILockBytes::writePageAt (std::shared_ptr<PageData> const & rPage, sal_uInt32 nOffset)
+{
+ // [SECURITY:ValInput]
+ PageData const * pagedata = rPage.get();
+ OSL_PRECOND(!(pagedata == nullptr), "store::ILockBytes::writePageAt(): invalid Page");
+ if (pagedata == nullptr)
+ return store_E_InvalidParameter;
+
+ sal_uInt32 const offset = pagedata->location();
+ OSL_PRECOND(!(nOffset != offset), "store::ILockBytes::writePageAt(): inconsistent Offset");
+ if (nOffset != offset)
+ return store_E_InvalidParameter;
+
+ OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::ILockBytes::writePageAt(): invalid Offset");
+ if (nOffset == STORE_PAGE_NULL)
+ return store_E_CantSeek;
+
+ return writePageAt_Impl (rPage, nOffset);
+}
+
+storeError ILockBytes::readAt (sal_uInt32 nOffset, void * pBuffer, sal_uInt32 nBytes)
+{
+ // [SECURITY:ValInput]
+ sal_uInt8 * dst_lo = static_cast<sal_uInt8*>(pBuffer);
+ if (dst_lo == nullptr)
+ return store_E_InvalidParameter;
+
+ sal_uInt8 * dst_hi = dst_lo + nBytes;
+ if (dst_lo >= dst_hi)
+ return (dst_lo > dst_hi) ? store_E_InvalidParameter : store_E_None;
+
+ OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::ILockBytes::readAt(): invalid Offset");
+ if (nOffset == STORE_PAGE_NULL)
+ return store_E_CantSeek;
+
+ sal_uInt64 const src_size = static_cast<sal_uInt64>(nOffset) + nBytes;
+ if (src_size > SAL_MAX_UINT32)
+ return store_E_CantSeek;
+
+ return readAt_Impl (nOffset, dst_lo, nBytes);
+}
+
+storeError ILockBytes::writeAt (sal_uInt32 nOffset, void const * pBuffer, sal_uInt32 nBytes)
+{
+ // [SECURITY:ValInput]
+ sal_uInt8 const * src_lo = static_cast<sal_uInt8 const*>(pBuffer);
+ if (src_lo == nullptr)
+ return store_E_InvalidParameter;
+
+ sal_uInt8 const * src_hi = src_lo + nBytes;
+ if (src_lo >= src_hi)
+ return (src_lo > src_hi) ? store_E_InvalidParameter : store_E_None;
+
+ OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::ILockBytes::writeAt(): invalid Offset");
+ if (nOffset == STORE_PAGE_NULL)
+ return store_E_CantSeek;
+
+ sal_uInt64 const dst_size = static_cast<sal_uInt64>(nOffset) + nBytes;
+ if (dst_size > SAL_MAX_UINT32)
+ return store_E_CantSeek;
+
+ return writeAt_Impl (nOffset, src_lo, nBytes);
+}
+
+storeError ILockBytes::getSize (sal_uInt32 & rnSize)
+{
+ rnSize = 0;
+ return getSize_Impl (rnSize);
+}
+
+storeError ILockBytes::setSize (sal_uInt32 nSize)
+{
+ return setSize_Impl (nSize);
+}
+
+storeError ILockBytes::flush()
+{
+ return flush_Impl();
+}
+
+namespace store
+{
+
+namespace {
+
+struct FileHandle
+{
+ oslFileHandle m_handle;
+
+ FileHandle() : m_handle(nullptr) {}
+
+ bool operator != (FileHandle const & rhs)
+ {
+ return (m_handle != rhs.m_handle);
+ }
+
+ static storeError errorFromNative (oslFileError eErrno)
+ {
+ switch (eErrno)
+ {
+ case osl_File_E_None:
+ return store_E_None;
+
+ case osl_File_E_NOENT:
+ return store_E_NotExists;
+
+ case osl_File_E_ACCES:
+ case osl_File_E_PERM:
+ return store_E_AccessViolation;
+
+ case osl_File_E_AGAIN:
+ case osl_File_E_DEADLK:
+ return store_E_LockingViolation;
+
+ case osl_File_E_BADF:
+ return store_E_InvalidHandle;
+
+ case osl_File_E_INVAL:
+ return store_E_InvalidParameter;
+
+ case osl_File_E_NOMEM:
+ return store_E_OutOfMemory;
+
+ case osl_File_E_NOSPC:
+ return store_E_OutOfSpace;
+
+ case osl_File_E_OVERFLOW:
+ return store_E_CantSeek;
+
+ default:
+ return store_E_Unknown;
+ }
+ }
+
+ static sal_uInt32 modeToNative (storeAccessMode eAccessMode)
+ {
+ sal_uInt32 nFlags = 0;
+ switch (eAccessMode)
+ {
+ case storeAccessMode::Create:
+ nFlags |= osl_File_OpenFlag_Create;
+ [[fallthrough]];
+ case storeAccessMode::ReadWrite:
+ nFlags |= osl_File_OpenFlag_Write;
+ [[fallthrough]];
+ case storeAccessMode::ReadOnly:
+ nFlags |= osl_File_OpenFlag_Read;
+ break;
+ default:
+ OSL_PRECOND(false, "store::FileHandle: unknown storeAccessMode");
+ }
+ return nFlags;
+ }
+
+ storeError initialize (rtl_uString * pFilename, storeAccessMode eAccessMode)
+ {
+ // Verify arguments.
+ sal_uInt32 nFlags = modeToNative (eAccessMode);
+ if (!pFilename || !nFlags)
+ return store_E_InvalidParameter;
+
+ // Convert into FileUrl.
+ OUString aFileUrl;
+ if (osl_getFileURLFromSystemPath (pFilename, &(aFileUrl.pData)) != osl_File_E_None)
+ {
+ // Not system path. Assume file url.
+ rtl_uString_assign (&(aFileUrl.pData), pFilename);
+ }
+ if (!aFileUrl.startsWith("file://"))
+ {
+ // Not file url. Assume relative path.
+ OUString aCwdUrl;
+ (void) osl_getProcessWorkingDir (&(aCwdUrl.pData));
+
+ // Absolute file url.
+ (void) osl_getAbsoluteFileURL (aCwdUrl.pData, aFileUrl.pData, &(aFileUrl.pData));
+ }
+
+ // Acquire handle.
+ oslFileError result = osl_openFile (aFileUrl.pData, &m_handle, nFlags);
+ if (result == osl_File_E_EXIST)
+ {
+ // Already existing (O_CREAT | O_EXCL).
+ result = osl_openFile (aFileUrl.pData, &m_handle, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write);
+ if ((result == osl_File_E_None) && (eAccessMode == storeAccessMode::Create))
+ {
+ // Truncate existing file.
+ result = osl_setFileSize (m_handle, 0);
+ }
+ }
+ if (result != osl_File_E_None)
+ return errorFromNative(result);
+ return store_E_None;
+ }
+
+ /** @see FileLockBytes destructor
+ */
+ static void closeFile (oslFileHandle hFile)
+ {
+ (void) osl_closeFile (hFile);
+ }
+
+ /** @see ResourceHolder<T>::destructor_type
+ */
+ struct CloseFile
+ {
+ void operator()(FileHandle & rFile) const
+ {
+ // Release handle.
+ closeFile (rFile.m_handle);
+ rFile.m_handle = nullptr;
+ }
+ };
+ typedef CloseFile destructor_type;
+};
+
+class FileLockBytes :
+ public store::OStoreObject,
+ public store::ILockBytes
+{
+ /** Representation.
+ */
+ oslFileHandle m_hFile;
+ sal_uInt32 m_nSize;
+ rtl::Reference< PageData::Allocator > m_xAllocator;
+
+ storeError initSize_Impl (sal_uInt32 & rnSize);
+
+ /** ILockBytes implementation.
+ */
+ virtual storeError initialize_Impl (rtl::Reference< PageData::Allocator > & rxAllocator, sal_uInt16 nPageSize) override;
+
+ virtual storeError readPageAt_Impl (std::shared_ptr<PageData> & rPage, sal_uInt32 nOffset) override;
+ virtual storeError writePageAt_Impl (std::shared_ptr<PageData> const & rPage, sal_uInt32 nOffset) override;
+
+ virtual storeError readAt_Impl (sal_uInt32 nOffset, void * pBuffer, sal_uInt32 nBytes) override;
+ virtual storeError writeAt_Impl (sal_uInt32 nOffset, void const * pBuffer, sal_uInt32 nBytes) override;
+
+ virtual storeError getSize_Impl (sal_uInt32 & rnSize) override;
+ virtual storeError setSize_Impl (sal_uInt32 nSize) override;
+
+ virtual storeError flush_Impl() override;
+
+public:
+ /** Construction.
+ */
+ explicit FileLockBytes (FileHandle const & rFile);
+
+ FileLockBytes(const FileLockBytes&) = delete;
+ FileLockBytes& operator=(const FileLockBytes&) = delete;
+
+protected:
+ /** Destruction.
+ */
+ virtual ~FileLockBytes() override;
+};
+
+}
+
+} // namespace store
+
+FileLockBytes::FileLockBytes (FileHandle const & rFile)
+ : m_hFile (rFile.m_handle), m_nSize (SAL_MAX_UINT32)
+{
+}
+
+FileLockBytes::~FileLockBytes()
+{
+ FileHandle::closeFile (m_hFile);
+}
+
+storeError FileLockBytes::initSize_Impl (sal_uInt32 & rnSize)
+{
+ /* osl_getFileSize() uses slow 'fstat(h, &size)',
+ * instead of fast 'size = lseek(h, 0, SEEK_END)'.
+ * so, init size here, and track changes.
+ */
+ sal_uInt64 uSize = 0;
+ oslFileError result = osl_getFileSize (m_hFile, &uSize);
+ if (result != osl_File_E_None)
+ return FileHandle::errorFromNative(result);
+ if (uSize > SAL_MAX_UINT32)
+ return store_E_CantSeek;
+
+ rnSize = sal::static_int_cast<sal_uInt32>(uSize);
+ return store_E_None;
+}
+
+storeError FileLockBytes::initialize_Impl (rtl::Reference< PageData::Allocator > & rxAllocator, sal_uInt16 nPageSize)
+{
+ storeError result = initSize_Impl (m_nSize);
+ if (result != store_E_None)
+ return result;
+
+ result = PageData::Allocator::createInstance (rxAllocator, nPageSize);
+ if (result != store_E_None)
+ return result;
+
+ // @see readPageAt_Impl().
+ m_xAllocator = rxAllocator;
+ return store_E_None;
+}
+
+storeError FileLockBytes::readPageAt_Impl (std::shared_ptr<PageData> & rPage, sal_uInt32 nOffset)
+{
+ if (m_xAllocator.is())
+ {
+ std::shared_ptr<PageData> page (m_xAllocator->construct<PageData>(), PageData::Deallocate(m_xAllocator));
+ page.swap (rPage);
+ }
+
+ if (!m_xAllocator.is())
+ return store_E_InvalidAccess;
+ if (!rPage)
+ return store_E_OutOfMemory;
+
+ PageData * pagedata = rPage.get();
+ return readAt_Impl (nOffset, pagedata, pagedata->size());
+}
+
+storeError FileLockBytes::writePageAt_Impl (std::shared_ptr<PageData> const & rPage, sal_uInt32 nOffset)
+{
+ PageData const * pagedata = rPage.get();
+ OSL_PRECOND(pagedata != nullptr, "contract violation");
+ return writeAt_Impl (nOffset, pagedata, pagedata->size());
+}
+
+storeError FileLockBytes::readAt_Impl (sal_uInt32 nOffset, void * pBuffer, sal_uInt32 nBytes)
+{
+ sal_uInt64 nDone = 0;
+ oslFileError result = osl_readFileAt (m_hFile, nOffset, pBuffer, nBytes, &nDone);
+ if (result != osl_File_E_None)
+ return FileHandle::errorFromNative(result);
+ if (nDone != nBytes)
+ return (nDone != 0) ? store_E_CantRead : store_E_NotExists;
+ return store_E_None;
+}
+
+storeError FileLockBytes::writeAt_Impl (sal_uInt32 nOffset, void const * pBuffer, sal_uInt32 nBytes)
+{
+ sal_uInt64 nDone = 0;
+ oslFileError result = osl_writeFileAt (m_hFile, nOffset, pBuffer, nBytes, &nDone);
+ if (result != osl_File_E_None)
+ return FileHandle::errorFromNative(result);
+ if (nDone != nBytes)
+ return store_E_CantWrite;
+
+ sal_uInt64 const uSize = nOffset + nBytes;
+ OSL_PRECOND(uSize < SAL_MAX_UINT32, "store::ILockBytes::writeAt() contract violation");
+ if (uSize > m_nSize)
+ m_nSize = sal::static_int_cast<sal_uInt32>(uSize);
+ return store_E_None;
+}
+
+storeError FileLockBytes::getSize_Impl (sal_uInt32 & rnSize)
+{
+ rnSize = m_nSize;
+ return store_E_None;
+}
+
+storeError FileLockBytes::setSize_Impl (sal_uInt32 nSize)
+{
+ oslFileError result = osl_setFileSize (m_hFile, nSize);
+ if (result != osl_File_E_None)
+ return FileHandle::errorFromNative(result);
+
+ m_nSize = nSize;
+ return store_E_None;
+}
+
+storeError FileLockBytes::flush_Impl()
+{
+ oslFileError result = osl_syncFile (m_hFile);
+ if (result != osl_File_E_None)
+ return FileHandle::errorFromNative(result);
+ return store_E_None;
+}
+
+/*========================================================================
+ *
+ * MappedLockBytes implementation.
+ *
+ *======================================================================*/
+namespace store
+{
+
+namespace {
+
+struct FileMapping
+{
+ sal_uInt8 * m_pAddr;
+ sal_uInt32 m_nSize;
+ oslFileHandle m_hFile;
+
+ FileMapping() : m_pAddr(nullptr), m_nSize(0), m_hFile(nullptr) {}
+
+ bool operator != (FileMapping const & rhs) const
+ {
+ return ((m_pAddr != rhs.m_pAddr) || (m_nSize != rhs.m_nSize));
+ }
+
+ oslFileError initialize (oslFileHandle hFile)
+ {
+ // Determine mapping size.
+ sal_uInt64 uSize = 0;
+ oslFileError result = osl_getFileSize (hFile, &uSize);
+ if (result != osl_File_E_None)
+ return result;
+
+ // [SECURITY:IntOver]
+ if (uSize > SAL_MAX_UINT32)
+ return osl_File_E_OVERFLOW;
+ m_nSize = sal::static_int_cast<sal_uInt32>(uSize);
+
+ m_hFile = hFile;
+
+ // Acquire mapping.
+ return osl_mapFile (hFile, reinterpret_cast<void**>(&m_pAddr), m_nSize, 0, osl_File_MapFlag_RandomAccess);
+ }
+
+ /** @see MappedLockBytes::destructor.
+ */
+ static void unmapFile (oslFileHandle hFile, sal_uInt8 * pAddr, sal_uInt32 nSize)
+ {
+ (void) osl_unmapMappedFile (hFile, pAddr, nSize);
+ (void) osl_closeFile (hFile);
+ }
+
+ /** @see ResourceHolder<T>::destructor_type
+ */
+ struct UnmapFile
+ {
+ void operator ()(FileMapping & rMapping) const
+ {
+ // Release mapping.
+ unmapFile (rMapping.m_hFile, rMapping.m_pAddr, rMapping.m_nSize);
+ rMapping.m_pAddr = nullptr;
+ rMapping.m_nSize = 0;
+ }
+ };
+ typedef UnmapFile destructor_type;
+};
+
+class MappedLockBytes :
+ public store::OStoreObject,
+ public store::PageData::Allocator,
+ public store::ILockBytes
+{
+ /** Representation.
+ */
+ sal_uInt8 * m_pData;
+ sal_uInt32 m_nSize;
+ sal_uInt16 m_nPageSize;
+ oslFileHandle m_hFile;
+
+ /** PageData::Allocator implementation.
+ */
+ virtual void allocate_Impl (void ** ppPage, sal_uInt16 * pnSize) override;
+ virtual void deallocate_Impl (void * pPage) override;
+
+ /** ILockBytes implementation.
+ */
+ virtual storeError initialize_Impl (rtl::Reference< PageData::Allocator > & rxAllocator, sal_uInt16 nPageSize) override;
+
+ virtual storeError readPageAt_Impl (std::shared_ptr<PageData> & rPage, sal_uInt32 nOffset) override;
+ virtual storeError writePageAt_Impl (std::shared_ptr<PageData> const & rPage, sal_uInt32 nOffset) override;
+
+ virtual storeError readAt_Impl (sal_uInt32 nOffset, void * pBuffer, sal_uInt32 nBytes) override;
+ virtual storeError writeAt_Impl (sal_uInt32 nOffset, const void * pBuffer, sal_uInt32 nBytes) override;
+
+ virtual storeError getSize_Impl (sal_uInt32 & rnSize) override;
+ virtual storeError setSize_Impl (sal_uInt32 nSize) override;
+
+ virtual storeError flush_Impl() override;
+
+public:
+ /** Construction.
+ */
+ explicit MappedLockBytes (FileMapping const & rMapping);
+
+ MappedLockBytes(const MappedLockBytes&) = delete;
+ MappedLockBytes& operator=(const MappedLockBytes&) = delete;
+
+protected:
+ /* Destruction.
+ */
+ virtual ~MappedLockBytes() override;
+};
+
+}
+
+} // namespace store
+
+MappedLockBytes::MappedLockBytes (FileMapping const & rMapping)
+ : m_pData (rMapping.m_pAddr), m_nSize (rMapping.m_nSize), m_nPageSize(0), m_hFile (rMapping.m_hFile)
+{
+}
+
+MappedLockBytes::~MappedLockBytes()
+{
+ FileMapping::unmapFile (m_hFile, m_pData, m_nSize);
+}
+
+void MappedLockBytes::allocate_Impl (void ** ppPage, sal_uInt16 * pnSize)
+{
+ OSL_PRECOND((ppPage != nullptr) && (pnSize != nullptr), "contract violation");
+ if ((ppPage != nullptr) && (pnSize != nullptr))
+ {
+ *ppPage = nullptr;
+ *pnSize = m_nPageSize;
+ }
+}
+
+void MappedLockBytes::deallocate_Impl (void * pPage)
+{
+ OSL_PRECOND((m_pData <= pPage) && (pPage < m_pData + m_nSize), "contract violation");
+}
+
+storeError MappedLockBytes::initialize_Impl (rtl::Reference< PageData::Allocator > & rxAllocator, sal_uInt16 nPageSize)
+{
+ rxAllocator = this;
+ m_nPageSize = nPageSize;
+ return store_E_None;
+}
+
+storeError MappedLockBytes::readPageAt_Impl (std::shared_ptr<PageData> & rPage, sal_uInt32 nOffset)
+{
+ sal_uInt8 * src_lo = m_pData + nOffset;
+ if ((m_pData > src_lo) || (src_lo >= m_pData + m_nSize))
+ return store_E_NotExists;
+
+ sal_uInt8 * src_hi = src_lo + m_nPageSize;
+ if ((m_pData > src_hi) || (src_hi > m_pData + m_nSize))
+ return store_E_CantRead;
+
+ std::shared_ptr<PageData> page (reinterpret_cast< PageData* >(src_lo), PageData::Deallocate(static_cast< PageData::Allocator* >(this)));
+ page.swap (rPage);
+
+ return store_E_None;
+}
+
+storeError MappedLockBytes::writePageAt_Impl (std::shared_ptr<PageData> const & /*rPage*/, sal_uInt32 /*nOffset*/)
+{
+ return store_E_AccessViolation;
+}
+
+storeError MappedLockBytes::readAt_Impl (sal_uInt32 nOffset, void * pBuffer, sal_uInt32 nBytes)
+{
+ sal_uInt8 const * src_lo = m_pData + nOffset;
+ if ((m_pData > src_lo) || (src_lo >= m_pData + m_nSize))
+ return store_E_NotExists;
+
+ sal_uInt8 const * src_hi = src_lo + nBytes;
+ if ((m_pData > src_hi) || (src_hi > m_pData + m_nSize))
+ return store_E_CantRead;
+
+ memcpy (pBuffer, src_lo, (src_hi - src_lo));
+ return store_E_None;
+}
+
+storeError MappedLockBytes::writeAt_Impl (sal_uInt32 /*nOffset*/, void const * /*pBuffer*/, sal_uInt32 /*nBytes*/)
+{
+ return store_E_AccessViolation;
+}
+
+storeError MappedLockBytes::getSize_Impl (sal_uInt32 & rnSize)
+{
+ rnSize = m_nSize;
+ return store_E_None;
+}
+
+storeError MappedLockBytes::setSize_Impl (sal_uInt32 /*nSize*/)
+{
+ return store_E_AccessViolation;
+}
+
+storeError MappedLockBytes::flush_Impl()
+{
+ return store_E_None;
+}
+
+namespace store
+{
+
+namespace {
+
+class MemoryLockBytes :
+ public store::OStoreObject,
+ public store::ILockBytes
+{
+ /** Representation.
+ */
+ sal_uInt8 * m_pData;
+ sal_uInt32 m_nSize;
+ rtl::Reference< PageData::Allocator > m_xAllocator;
+
+ /** ILockBytes implementation.
+ */
+ virtual storeError initialize_Impl (rtl::Reference< PageData::Allocator > & rxAllocator, sal_uInt16 nPageSize) override;
+
+ virtual storeError readPageAt_Impl (std::shared_ptr<PageData> & rPage, sal_uInt32 nOffset) override;
+ virtual storeError writePageAt_Impl (std::shared_ptr<PageData> const & rPage, sal_uInt32 nOffset) override;
+
+ virtual storeError readAt_Impl (sal_uInt32 nOffset, void * pBuffer, sal_uInt32 nBytes) override;
+ virtual storeError writeAt_Impl (sal_uInt32 nOffset, const void * pBuffer, sal_uInt32 nBytes) override;
+
+ virtual storeError getSize_Impl (sal_uInt32 & rnSize) override;
+ virtual storeError setSize_Impl (sal_uInt32 nSize) override;
+
+ virtual storeError flush_Impl() override;
+
+public:
+ /** Construction.
+ */
+ MemoryLockBytes();
+
+ MemoryLockBytes(const MemoryLockBytes&) = delete;
+ MemoryLockBytes& operator=(const MemoryLockBytes&) = delete;
+
+protected:
+ virtual ~MemoryLockBytes() override;
+};
+
+}
+
+} // namespace store
+
+MemoryLockBytes::MemoryLockBytes()
+ : m_pData (nullptr), m_nSize (0)
+{}
+
+MemoryLockBytes::~MemoryLockBytes()
+{
+ std::free (m_pData);
+}
+
+storeError MemoryLockBytes::initialize_Impl (rtl::Reference< PageData::Allocator > & rxAllocator, sal_uInt16 nPageSize)
+{
+ storeError result = PageData::Allocator::createInstance (rxAllocator, nPageSize);
+ if (result == store_E_None)
+ {
+ // @see readPageAt_Impl().
+ m_xAllocator = rxAllocator;
+ }
+ return result;
+}
+
+storeError MemoryLockBytes::readPageAt_Impl (std::shared_ptr<PageData> & rPage, sal_uInt32 nOffset)
+{
+ if (m_xAllocator.is())
+ {
+ std::shared_ptr<PageData> page (m_xAllocator->construct<PageData>(), PageData::Deallocate(m_xAllocator));
+ page.swap (rPage);
+ }
+
+ if (!m_xAllocator.is())
+ return store_E_InvalidAccess;
+ if (!rPage)
+ return store_E_OutOfMemory;
+
+ PageData * pagedata = rPage.get();
+ return readAt_Impl (nOffset, pagedata, pagedata->size());
+}
+
+storeError MemoryLockBytes::writePageAt_Impl (std::shared_ptr<PageData> const & rPage, sal_uInt32 nOffset)
+{
+ PageData const * pagedata = rPage.get();
+ OSL_PRECOND(!(pagedata == nullptr), "contract violation");
+ return writeAt_Impl (nOffset, pagedata, pagedata->size());
+}
+
+storeError MemoryLockBytes::readAt_Impl (sal_uInt32 nOffset, void * pBuffer, sal_uInt32 nBytes)
+{
+ sal_uInt8 const * src_lo = m_pData + nOffset;
+ if ((m_pData > src_lo) || (src_lo >= m_pData + m_nSize))
+ return store_E_NotExists;
+
+ sal_uInt8 const * src_hi = src_lo + nBytes;
+ if ((m_pData > src_hi) || (src_hi > m_pData + m_nSize))
+ return store_E_CantRead;
+
+ memcpy (pBuffer, src_lo, (src_hi - src_lo));
+ return store_E_None;
+}
+
+storeError MemoryLockBytes::writeAt_Impl (sal_uInt32 nOffset, const void * pBuffer, sal_uInt32 nBytes)
+{
+ sal_uInt64 const dst_size = nOffset + nBytes;
+ OSL_PRECOND(dst_size < SAL_MAX_UINT32, "store::ILockBytes::writeAt() contract violation");
+ if (dst_size > m_nSize)
+ {
+ storeError eErrCode = setSize_Impl (sal::static_int_cast<sal_uInt32>(dst_size));
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ }
+ SAL_WARN_IF(dst_size > m_nSize, "store", "store::MemoryLockBytes::setSize_Impl() contract violation");
+
+ sal_uInt8 * dst_lo = m_pData + nOffset;
+ if (dst_lo >= m_pData + m_nSize)
+ return store_E_CantSeek;
+
+ sal_uInt8 * dst_hi = dst_lo + nBytes;
+ if (dst_hi > m_pData + m_nSize)
+ return store_E_CantWrite;
+
+ memcpy (dst_lo, pBuffer, (dst_hi - dst_lo));
+ return store_E_None;
+}
+
+storeError MemoryLockBytes::getSize_Impl (sal_uInt32 & rnSize)
+{
+ rnSize = m_nSize;
+ return store_E_None;
+}
+
+storeError MemoryLockBytes::setSize_Impl (sal_uInt32 nSize)
+{
+ if (nSize != m_nSize)
+ {
+ sal_uInt8 * pData = static_cast<sal_uInt8*>(std::realloc (m_pData, nSize));
+ if (pData != nullptr)
+ {
+ if (nSize > m_nSize)
+ memset (pData + m_nSize, 0, sal::static_int_cast<size_t>(nSize - m_nSize));
+ }
+ else
+ {
+ if (nSize != 0)
+ return store_E_OutOfMemory;
+ }
+ m_pData = pData;
+ m_nSize = nSize;
+ }
+ return store_E_None;
+}
+
+storeError MemoryLockBytes::flush_Impl()
+{
+ return store_E_None;
+}
+
+// ILockBytes factory implementations.
+
+namespace store
+{
+
+namespace {
+
+template< class T > struct ResourceHolder
+{
+ typedef typename T::destructor_type destructor_type;
+
+ T m_value;
+
+ explicit ResourceHolder (T const & value = T()) : m_value (value) {}
+ ~ResourceHolder() { reset(T()); }
+
+ T & get() { return m_value; }
+ T const & get() const { return m_value; }
+
+ void set (T const & value) { m_value = value; }
+ void reset (T const & value)
+ {
+ T tmp (m_value);
+ if (tmp != value)
+ destructor_type()(tmp);
+ set (value);
+ }
+ T release()
+ {
+ T tmp (m_value);
+ set (T());
+ return tmp;
+ }
+
+ ResourceHolder & operator= (ResourceHolder & rhs)
+ {
+ reset (rhs.release());
+ return *this;
+ }
+};
+
+}
+
+storeError
+FileLockBytes_createInstance (
+ rtl::Reference< ILockBytes > & rxLockBytes,
+ rtl_uString * pFilename,
+ storeAccessMode eAccessMode
+)
+{
+ // Acquire file handle.
+ ResourceHolder<FileHandle> xFile;
+ storeError result = xFile.get().initialize (pFilename, eAccessMode);
+ if (result != store_E_None)
+ return result;
+
+ if (eAccessMode == storeAccessMode::ReadOnly)
+ {
+ ResourceHolder<FileMapping> xMapping;
+ if (xMapping.get().initialize (xFile.get().m_handle) == osl_File_E_None)
+ {
+ rxLockBytes = new MappedLockBytes (xMapping.get());
+ if (!rxLockBytes.is())
+ return store_E_OutOfMemory;
+ (void) xFile.release();
+ (void) xMapping.release();
+ }
+ }
+ if (!rxLockBytes.is())
+ {
+ rxLockBytes = new FileLockBytes (xFile.get());
+ if (!rxLockBytes.is())
+ return store_E_OutOfMemory;
+ (void) xFile.release();
+ }
+
+ return store_E_None;
+}
+
+storeError
+MemoryLockBytes_createInstance (
+ rtl::Reference< ILockBytes > & rxLockBytes
+)
+{
+ rxLockBytes = new MemoryLockBytes();
+ if (!rxLockBytes.is())
+ return store_E_OutOfMemory;
+
+ return store_E_None;
+}
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/lockbyte.hxx b/store/source/lockbyte.hxx
new file mode 100644
index 0000000000..41c6145f16
--- /dev/null
+++ b/store/source/lockbyte.hxx
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <sal/types.h>
+
+#include <rtl/ustring.h>
+#include <salhelper/simplereferenceobject.hxx>
+
+#include <store/types.h>
+#include "storbase.hxx"
+
+namespace rtl { template <class reference_type> class Reference; }
+
+namespace store
+{
+
+class ILockBytes : public virtual salhelper::SimpleReferenceObject
+{
+public:
+ /**
+ @param rxAllocator [out]
+ @param nPageSize [in]
+ */
+ storeError initialize (
+ rtl::Reference< PageData::Allocator > & rxAllocator,
+ sal_uInt16 nPageSize);
+
+ /**
+ @param rPage [out]
+ @param nOffset [in]
+ */
+ storeError readPageAt (
+ std::shared_ptr<PageData> & rPage,
+ sal_uInt32 nOffset);
+
+ /**
+ @param rPage [in]
+ @param nOffset [in]
+ */
+ storeError writePageAt (
+ std::shared_ptr<PageData> const & rPage,
+ sal_uInt32 nOffset);
+
+ /**
+ @param nOffset [in]
+ @param pBuffer [out]
+ @param nBytes [in]
+ @return store_E_None upon success
+ */
+ storeError readAt (
+ sal_uInt32 nOffset,
+ void *pBuffer,
+ sal_uInt32 nBytes);
+
+ /**
+ @param nOffset [in]
+ @param pBuffer [in]
+ @param nBytes [in]
+ @return store_E_None upon success
+ */
+ storeError writeAt (
+ sal_uInt32 nOffset,
+ const void *pBuffer,
+ sal_uInt32 nBytes);
+
+ /**
+ @param rnSize [out]
+ @return store_E_None upon success
+ */
+ storeError getSize (sal_uInt32 & rnSize);
+
+ /**
+ @param nSize [in]
+ @return store_E_None upon success
+ */
+ storeError setSize (sal_uInt32 nSize);
+
+ /**
+ @return store_E_None upon success
+ */
+ storeError flush();
+
+protected:
+ virtual ~ILockBytes() override {}
+
+private:
+ /** Implementation (abstract).
+ */
+ virtual storeError initialize_Impl (
+ rtl::Reference< PageData::Allocator > & rxAllocator,
+ sal_uInt16 nPageSize) = 0;
+
+ virtual storeError readPageAt_Impl (
+ std::shared_ptr<PageData> & rPage,
+ sal_uInt32 nOffset) = 0;
+
+ virtual storeError writePageAt_Impl (
+ std::shared_ptr<PageData> const & rPage,
+ sal_uInt32 nOffset) = 0;
+
+ virtual storeError readAt_Impl (
+ sal_uInt32 nOffset,
+ void *pBuffer,
+ sal_uInt32 nBytes) = 0;
+
+ virtual storeError writeAt_Impl (
+ sal_uInt32 nOffset,
+ const void *pBuffer,
+ sal_uInt32 nBytes) = 0;
+
+ virtual storeError getSize_Impl (
+ sal_uInt32 & rnSize) = 0;
+
+ virtual storeError setSize_Impl (
+ sal_uInt32 nSize) = 0;
+
+ virtual storeError flush_Impl() = 0;
+};
+
+storeError FileLockBytes_createInstance (
+ rtl::Reference< store::ILockBytes > & rxLockBytes,
+ rtl_uString * pFilename,
+ storeAccessMode eAccessMode
+);
+
+storeError MemoryLockBytes_createInstance (
+ rtl::Reference< store::ILockBytes > & rxLockBytes
+);
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/object.cxx b/store/source/object.cxx
new file mode 100644
index 0000000000..956c17e177
--- /dev/null
+++ b/store/source/object.cxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "object.hxx"
+
+#include <sal/types.h>
+
+namespace store
+{
+const sal_uInt32 OStoreObject::m_nTypeId = sal_uInt32(0x58190322);
+
+bool OStoreObject::isKindOf(sal_uInt32 nTypeId) { return (nTypeId == m_nTypeId); }
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/object.hxx b/store/source/object.hxx
new file mode 100644
index 0000000000..c68f9b8f8a
--- /dev/null
+++ b/store/source/object.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <salhelper/simplereferenceobject.hxx>
+
+namespace store
+{
+class OStoreObject : public virtual salhelper::SimpleReferenceObject
+{
+public:
+ OStoreObject() {}
+
+ /** Replaces dynamic_cast type checking.
+ */
+ virtual bool isKindOf(sal_uInt32 nTypeId);
+
+protected:
+ virtual ~OStoreObject() override {}
+
+private:
+ /** The IStoreHandle TypeId.
+ */
+ static const sal_uInt32 m_nTypeId;
+ OStoreObject(const OStoreObject&) = delete;
+ OStoreObject& operator=(const OStoreObject&) = delete;
+};
+
+/** Template helper function as dynamic_cast replacement.
+ */
+template <class store_handle_type>
+store_handle_type* SAL_CALL query(OStoreObject* pHandle, store_handle_type*);
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storbase.cxx b/store/source/storbase.cxx
new file mode 100644
index 0000000000..63c6a3466c
--- /dev/null
+++ b/store/source/storbase.cxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "storbase.hxx"
+
+#include <sal/types.h>
+#include <rtl/alloc.h>
+#include <rtl/ref.hxx>
+#include <osl/diagnose.h>
+
+#include <store/types.h>
+#include "object.hxx"
+
+#include <stdio.h>
+
+using namespace store;
+
+// PageData::Allocator_Impl (default allocator).
+
+namespace store
+{
+
+class PageData::Allocator_Impl :
+ public store::OStoreObject,
+ public store::PageData::Allocator
+{
+public:
+ /** Construction (two phase).
+ */
+ Allocator_Impl();
+
+ Allocator_Impl(const Allocator_Impl&) = delete;
+ Allocator_Impl& operator=(const Allocator_Impl&) = delete;
+
+ storeError initialize (sal_uInt16 nPageSize);
+
+protected:
+ virtual ~Allocator_Impl() override;
+
+private:
+ rtl_cache_type * m_page_cache;
+ sal_uInt16 m_page_size;
+
+ /** PageData::Allocator implementation.
+ */
+ virtual void allocate_Impl (void ** ppPage, sal_uInt16 * pnSize) override;
+ virtual void deallocate_Impl (void * pPage) override;
+};
+
+} // namespace store
+
+PageData::Allocator_Impl::Allocator_Impl()
+ : m_page_cache(nullptr), m_page_size(0)
+{}
+
+storeError
+PageData::Allocator_Impl::initialize (sal_uInt16 nPageSize)
+{
+ char name[RTL_CACHE_NAME_LENGTH + 1];
+ std::size_t size = sal::static_int_cast<std::size_t>(nPageSize);
+ (void) snprintf (name, sizeof(name), "store_page_alloc_%" SAL_PRI_SIZET "u", size);
+
+ m_page_cache = rtl_cache_create (name, size, 0, nullptr, nullptr, nullptr, nullptr, nullptr, 0);
+ if (!m_page_cache)
+ return store_E_OutOfMemory;
+
+ m_page_size = nPageSize;
+ return store_E_None;
+}
+
+PageData::Allocator_Impl::~Allocator_Impl()
+{
+ rtl_cache_destroy(m_page_cache);
+ m_page_cache = nullptr;
+}
+
+void PageData::Allocator_Impl::allocate_Impl (void ** ppPage, sal_uInt16 * pnSize)
+{
+ OSL_PRECOND((ppPage != nullptr) && (pnSize != nullptr), "contract violation");
+ if ((ppPage != nullptr) && (pnSize != nullptr))
+ {
+ *ppPage = rtl_cache_alloc(m_page_cache);
+ *pnSize = m_page_size;
+ }
+}
+
+void PageData::Allocator_Impl::deallocate_Impl (void * pPage)
+{
+ OSL_PRECOND(pPage != nullptr, "contract violation");
+ rtl_cache_free(m_page_cache, pPage);
+}
+
+storeError
+PageData::Allocator::createInstance (rtl::Reference< PageData::Allocator > & rxAllocator, sal_uInt16 nPageSize)
+{
+ rtl::Reference< PageData::Allocator_Impl > xAllocator (new PageData::Allocator_Impl());
+ if (!xAllocator.is())
+ return store_E_OutOfMemory;
+
+ rxAllocator = &*xAllocator;
+ return xAllocator->initialize (nPageSize);
+}
+
+OStorePageObject::~OStorePageObject()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storbase.hxx b/store/source/storbase.hxx
new file mode 100644
index 0000000000..207cbf40a8
--- /dev/null
+++ b/store/source/storbase.hxx
@@ -0,0 +1,608 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <salhelper/simplereferenceobject.hxx>
+
+#include <sal/types.h>
+
+#include <rtl/crc.h>
+#include <rtl/ref.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/endian.h>
+
+#include <store/types.h>
+
+#include <cstdlib>
+#include <memory>
+#include <utility>
+
+/** @file store common internals.
+*/
+
+namespace store
+{
+
+#ifdef htons
+#undef htons
+#endif
+#ifdef ntohs
+#undef ntohs
+#endif
+
+#ifdef htonl
+#undef htonl
+#endif
+#ifdef ntohl
+#undef ntohl
+#endif
+
+#ifdef OSL_BIGENDIAN
+inline sal_uInt16 htons (sal_uInt16 h) { return OSL_SWAPWORD(h); }
+inline sal_uInt16 ntohs (sal_uInt16 n) { return OSL_SWAPWORD(n); }
+
+inline sal_uInt32 htonl (sal_uInt32 h) { return OSL_SWAPDWORD(h); }
+inline sal_uInt32 ntohl (sal_uInt32 n) { return OSL_SWAPDWORD(n); }
+#else
+inline sal_uInt16 htons (sal_uInt16 h) { return h; }
+inline sal_uInt16 ntohs (sal_uInt16 n) { return n; }
+
+inline sal_uInt32 htonl (sal_uInt32 h) { return h; }
+inline sal_uInt32 ntohl (sal_uInt32 n) { return n; }
+#endif /* OSL_BIGENDIAN */
+
+struct OStorePageGuard
+{
+ /** Representation.
+ */
+ sal_uInt32 m_nMagic;
+ sal_uInt32 m_nCRC32;
+
+ /** Construction.
+ */
+ explicit OStorePageGuard (sal_uInt32 nMagic = 0)
+ : m_nMagic (store::htonl(nMagic)),
+ m_nCRC32 (store::htonl(0))
+ {}
+
+ void swap (OStorePageGuard & rhs)
+ {
+ std::swap(m_nMagic, rhs.m_nMagic);
+ std::swap(m_nCRC32, rhs.m_nCRC32);
+ }
+
+ OStorePageGuard (OStorePageGuard const & rhs)
+ : m_nMagic (rhs.m_nMagic),
+ m_nCRC32 (rhs.m_nCRC32)
+ {}
+
+ OStorePageGuard& operator= (const OStorePageGuard& rhs)
+ {
+ m_nMagic = rhs.m_nMagic;
+ m_nCRC32 = rhs.m_nCRC32;
+ return *this;
+ }
+
+ /** Comparison.
+ */
+ bool operator== (const OStorePageGuard& rhs) const
+ {
+ return ((m_nMagic == rhs.m_nMagic) &&
+ (m_nCRC32 == rhs.m_nCRC32) );
+ }
+};
+
+#define STORE_PAGE_NULL (sal_uInt32(~0))
+
+struct OStorePageDescriptor
+{
+ /** Representation.
+ */
+ sal_uInt32 m_nAddr;
+ sal_uInt16 m_nSize;
+ sal_uInt16 m_nUsed;
+
+ /** Construction.
+ */
+ explicit OStorePageDescriptor (
+ sal_uInt32 nAddr,
+ sal_uInt16 nSize,
+ sal_uInt16 nUsed)
+ : m_nAddr (store::htonl(nAddr)),
+ m_nSize (store::htons(nSize)),
+ m_nUsed (store::htons(nUsed))
+ {}
+
+ void swap (OStorePageDescriptor & rhs)
+ {
+ std::swap(m_nAddr, rhs.m_nAddr);
+ std::swap(m_nSize, rhs.m_nSize);
+ std::swap(m_nUsed, rhs.m_nUsed);
+ }
+
+ OStorePageDescriptor (const OStorePageDescriptor & rhs)
+ : m_nAddr (rhs.m_nAddr),
+ m_nSize (rhs.m_nSize),
+ m_nUsed (rhs.m_nUsed)
+ {}
+
+ OStorePageDescriptor & operator= (const OStorePageDescriptor & rhs)
+ {
+ m_nAddr = rhs.m_nAddr;
+ m_nSize = rhs.m_nSize;
+ m_nUsed = rhs.m_nUsed;
+ return *this;
+ }
+
+ /** Comparison.
+ */
+ bool operator== (const OStorePageDescriptor & rhs) const
+ {
+ return ((m_nAddr == rhs.m_nAddr) &&
+ (m_nSize == rhs.m_nSize) );
+ }
+};
+
+struct OStorePageKey
+{
+ /** Representation.
+ */
+ sal_uInt32 m_nLow;
+ sal_uInt32 m_nHigh;
+
+ /** Construction.
+ */
+ explicit OStorePageKey (sal_uInt32 nLow = 0, sal_uInt32 nHigh = 0)
+ : m_nLow (store::htonl(nLow)),
+ m_nHigh (store::htonl(nHigh))
+ {}
+
+ /** Comparison.
+ */
+ bool operator== (const OStorePageKey & rhs) const
+ {
+ return ((m_nLow == rhs.m_nLow ) &&
+ (m_nHigh == rhs.m_nHigh) );
+ }
+
+ bool operator< (const OStorePageKey & rhs) const
+ {
+ if (m_nHigh == rhs.m_nHigh)
+ return (store::ntohl(m_nLow) < store::ntohl(rhs.m_nLow));
+ else
+ return (store::ntohl(m_nHigh) < store::ntohl(rhs.m_nHigh));
+ }
+};
+
+struct OStorePageLink
+{
+ /** Representation.
+ */
+ sal_uInt32 m_nAddr;
+
+ /** Construction.
+ */
+ explicit OStorePageLink (sal_uInt32 nAddr = STORE_PAGE_NULL)
+ : m_nAddr (store::htonl(nAddr))
+ {}
+
+ void swap (OStorePageLink & rhs)
+ {
+ std::swap(m_nAddr, rhs.m_nAddr);
+ }
+
+ OStorePageLink & operator= (sal_uInt32 nAddr)
+ {
+ m_nAddr = store::htonl(nAddr);
+ return *this;
+ }
+
+ /** Comparison.
+ */
+ bool operator== (const OStorePageLink & rhs) const
+ {
+ return (m_nAddr == rhs.m_nAddr);
+ }
+
+ /** Operation.
+ */
+ sal_uInt32 location() const
+ {
+ return store::ntohl(m_nAddr);
+ }
+
+};
+
+struct PageData
+{
+ typedef OStorePageGuard G;
+ typedef OStorePageDescriptor D;
+ typedef OStorePageLink L;
+
+ /** Representation.
+ */
+ G m_aGuard;
+ D m_aDescr;
+ L m_aMarked;
+ L m_aUnused;
+
+ /** theSize.
+ */
+ static const size_t theSize = sizeof(G) + sizeof(D) + 2 * sizeof(L);
+ static const sal_uInt16 thePageSize = theSize;
+ static_assert(STORE_MINIMUM_PAGESIZE >= thePageSize, "must be at least thePageSize");
+
+ /** location.
+ */
+ sal_uInt32 location() const
+ {
+ return store::ntohl(m_aDescr.m_nAddr);
+ }
+ void location (sal_uInt32 nAddr)
+ {
+ m_aDescr.m_nAddr = store::htonl(nAddr);
+ }
+
+ /** size.
+ */
+ sal_uInt16 size() const
+ {
+ return store::ntohs(m_aDescr.m_nSize);
+ }
+
+ /** type.
+ */
+ sal_uInt32 type() const
+ {
+ return store::ntohl(m_aGuard.m_nMagic);
+ }
+
+ /** Allocation.
+ */
+ class Allocator_Impl;
+ class Allocator : public virtual salhelper::SimpleReferenceObject
+ {
+ public:
+ template< class T > T * construct()
+ {
+ void * page = nullptr;
+ sal_uInt16 nSize = 0;
+ if (allocate (&page, &nSize))
+ {
+ return new(page) T(nSize);
+ }
+ return nullptr;
+ }
+
+ bool allocate (void ** ppPage, sal_uInt16 * pnSize)
+ {
+ allocate_Impl (ppPage, pnSize);
+ return ((*ppPage != nullptr) && (*pnSize != 0));
+ }
+
+ void deallocate (void * pPage)
+ {
+ if (pPage != nullptr)
+ deallocate_Impl (pPage);
+ }
+
+ static storeError createInstance (
+ rtl::Reference< PageData::Allocator > & rxAllocator, sal_uInt16 nPageSize);
+
+ protected:
+ virtual ~Allocator() override {}
+
+ private:
+ /** Implementation (abstract).
+ */
+ virtual void allocate_Impl (void ** ppPage, sal_uInt16 * pnSize) = 0;
+ virtual void deallocate_Impl (void * pPage) = 0;
+ };
+
+ class Deallocate {
+ public:
+ explicit Deallocate(rtl::Reference<Allocator> allocator):
+ allocator_(std::move(allocator)) {};
+
+ void operator ()(void * page) const { allocator_->deallocate(page); }
+
+ private:
+ rtl::Reference<Allocator> allocator_;
+ };
+
+ static void* operator new (size_t, void * p) { return p; }
+ static void operator delete (void * , void *) {}
+
+ /** Construction.
+ */
+ explicit PageData (sal_uInt16 nPageSize = thePageSize)
+ : m_aGuard(),
+ m_aDescr(STORE_PAGE_NULL, nPageSize, thePageSize),
+ m_aMarked(),
+ m_aUnused()
+ {}
+
+ void swap (PageData & rhs) // nothrow
+ {
+ m_aGuard.swap(rhs.m_aGuard);
+ m_aDescr.swap(rhs.m_aDescr);
+ m_aMarked.swap(rhs.m_aMarked);
+ m_aUnused.swap(rhs.m_aUnused);
+ }
+
+ PageData (PageData const & rhs) // nothrow
+ : m_aGuard (rhs.m_aGuard),
+ m_aDescr (rhs.m_aDescr),
+ m_aMarked(rhs.m_aMarked),
+ m_aUnused(rhs.m_aUnused)
+ {}
+
+ PageData & operator= (PageData const & rhs) // nothrow
+ {
+ PageData tmp (rhs);
+ swap (tmp);
+ return *this;
+ }
+
+ /** guard (external representation).
+ */
+ void guard (sal_uInt32 nAddr)
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ m_aDescr.m_nAddr = store::htonl(nAddr);
+ nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ m_aGuard.m_nCRC32 = store::htonl(nCRC32);
+ }
+
+ /** verify (external representation).
+ */
+ storeError verify (sal_uInt32 nAddr) const
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
+ return store_E_InvalidChecksum;
+ if (m_aDescr.m_nAddr != store::htonl(nAddr))
+ return store_E_InvalidAccess;
+ return store_E_None;
+ }
+
+};
+
+template< class T >
+class PageHolderObject
+{
+ /** Representation.
+ */
+ std::shared_ptr<PageData> m_xPage;
+
+ /** Checked cast.
+ */
+ template< class U >
+ static bool isA (PageData const * p)
+ {
+ return ((p != nullptr) && (p->type() == U::theTypeId));
+ }
+
+ template< class U >
+ static U * dynamic_page_cast (PageData * p)
+ {
+ return isA<U>(p) ? static_cast<U*>(p) : nullptr;
+ }
+
+ template< class U >
+ static U const * dynamic_page_cast (PageData const * p)
+ {
+ return isA<U>(p) ? static_cast<U const *>(p) : nullptr;
+ }
+
+public:
+ bool construct (rtl::Reference< PageData::Allocator > const & rxAllocator)
+ {
+ if (!m_xPage && rxAllocator.is())
+ {
+ std::shared_ptr<PageData> tmp (rxAllocator->construct<T>(), PageData::Deallocate(rxAllocator));
+ m_xPage.swap (tmp);
+ }
+ return bool(m_xPage);
+ }
+
+ explicit PageHolderObject (std::shared_ptr<PageData> xPage = std::shared_ptr<PageData>())
+ : m_xPage (std::move(xPage))
+ {}
+
+ void swap (PageHolderObject<T> & rhs)
+ {
+ m_xPage.swap (rhs.m_xPage);
+ }
+
+ PageHolderObject (PageHolderObject<T> const & rhs)
+ : m_xPage (rhs.m_xPage)
+ {}
+
+ PageHolderObject<T> & operator= (PageHolderObject<T> const & rhs)
+ {
+ PageHolderObject<T> tmp (rhs);
+ this->swap (tmp);
+ return *this;
+ }
+
+ bool is() const
+ {
+ return bool(m_xPage);
+ }
+
+ std::shared_ptr<PageData> & get() { return m_xPage; }
+ std::shared_ptr<PageData> const & get() const { return m_xPage; }
+
+ T * operator->()
+ {
+ T * pImpl = dynamic_page_cast<T>(m_xPage.get());
+ OSL_PRECOND(pImpl != nullptr, "store::PageHolder<T>::operator*(): Null pointer");
+ return pImpl;
+ }
+
+ T const * operator->() const
+ {
+ T const * pImpl = dynamic_page_cast<T>(m_xPage.get());
+ OSL_PRECOND(pImpl != nullptr, "store::PageHolder<T>::operator*(): Null pointer");
+ return pImpl;
+ }
+
+ T & operator*()
+ {
+ T * pImpl = dynamic_page_cast<T>(m_xPage.get());
+ OSL_PRECOND(pImpl != nullptr, "store::PageHolder<T>::operator*(): Null pointer");
+ return (*pImpl);
+ }
+
+ T const & operator*() const
+ {
+ T const * pImpl = dynamic_page_cast<T>(m_xPage.get());
+ OSL_PRECOND(pImpl != nullptr, "store::PageHolder<T>::operator*(): Null pointer");
+ return (*pImpl);
+ }
+
+ static storeError guard (std::shared_ptr<PageData> const & rxPage, sal_uInt32 nAddr)
+ {
+ PageData * pHead = rxPage.get();
+ if (!pHead)
+ return store_E_InvalidAccess;
+ pHead->guard(nAddr);
+
+ T * pImpl = dynamic_page_cast<T>(pHead);
+ OSL_PRECOND(pImpl != nullptr, "store::PageHolder<T>::guard(): Null pointer");
+ pImpl->guard();
+
+ return store_E_None;
+ }
+
+ static storeError verify (std::shared_ptr<PageData> const & rxPage, sal_uInt32 nAddr)
+ {
+ PageData const * pHead = rxPage.get();
+ if (!pHead)
+ return store_E_InvalidAccess;
+
+ storeError eErrCode = pHead->verify(nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ T const * pImpl = dynamic_page_cast<T>(pHead);
+ if (!pImpl)
+ return store_E_WrongVersion;
+
+ return pImpl->verify();
+ }
+};
+
+class OStorePageObject
+{
+ typedef PageData page;
+
+public:
+ /** Allocation.
+ */
+ static void * operator new (size_t n)
+ {
+ return std::malloc(sal_uInt32(n));
+ }
+ static void operator delete (void * p)
+ {
+ std::free (p);
+ }
+
+ /** State.
+ */
+ inline bool dirty() const;
+ inline void clean();
+ inline void touch();
+
+ /** Location.
+ */
+ inline sal_uInt32 location() const;
+
+protected:
+ /** Representation.
+ */
+ std::shared_ptr<PageData> m_xPage;
+ bool m_bDirty;
+
+ /** Construction.
+ */
+ explicit OStorePageObject (std::shared_ptr<PageData> rxPage)
+ : m_xPage (std::move(rxPage)), m_bDirty (false)
+ {}
+
+ /** Destruction.
+ */
+ virtual ~OStorePageObject();
+
+public:
+ template< class U >
+ PageHolderObject<U> makeHolder() const
+ {
+ return PageHolderObject<U>(m_xPage);
+ }
+
+ template< class U >
+ storeError construct (rtl::Reference< PageData::Allocator > const & rxAllocator)
+ {
+ if (!rxAllocator.is())
+ return store_E_InvalidAccess;
+
+ std::shared_ptr<PageData> tmp (rxAllocator->construct<U>(), PageData::Deallocate(rxAllocator));
+ if (!tmp)
+ return store_E_OutOfMemory;
+
+ m_xPage.swap (tmp);
+ return store_E_None;
+ }
+
+ std::shared_ptr<PageData> & get() { return m_xPage; }
+
+ virtual storeError guard (sal_uInt32 nAddr) = 0;
+ virtual storeError verify (sal_uInt32 nAddr) const = 0;
+};
+
+inline bool OStorePageObject::dirty() const
+{
+ return m_bDirty;
+}
+
+inline void OStorePageObject::clean()
+{
+ m_bDirty = false;
+}
+
+inline void OStorePageObject::touch()
+{
+ m_bDirty = true;
+}
+
+inline sal_uInt32 OStorePageObject::location() const
+{
+ return m_xPage->location();
+}
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storbios.cxx b/store/source/storbios.cxx
new file mode 100644
index 0000000000..af74091632
--- /dev/null
+++ b/store/source/storbios.cxx
@@ -0,0 +1,890 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include "storbios.hxx"
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+
+#include <rtl/alloc.h>
+#include <rtl/ref.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+
+#include <store/types.h>
+#include "lockbyte.hxx"
+#include "storcach.hxx"
+
+using namespace store;
+
+constexpr sal_uInt32 STORE_MAGIC_SUPERBLOCK = 0x484D5343;
+
+namespace {
+
+struct OStoreSuperBlock
+{
+ typedef OStorePageGuard G;
+ typedef OStorePageDescriptor D;
+ typedef OStorePageLink L;
+
+ G m_aGuard;
+ D m_aDescr;
+ sal_uInt32 m_nMarked;
+ L m_aMarked;
+ sal_uInt32 m_nUnused;
+ L m_aUnused;
+
+ static const size_t theSize = sizeof(G) + sizeof(D) + 2 * (sizeof(L) + sizeof(sal_uInt32));
+
+ 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)
+ {}
+
+ 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) );
+ }
+
+ sal_uInt32 unusedCount() const
+ {
+ return store::ntohl(m_nUnused);
+ }
+
+ const L& unusedHead() 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()
+ {
+ m_nUnused = store::htonl(0);
+ m_aUnused = L(0);
+ }
+
+ void guard()
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ m_aGuard.m_nCRC32 = store::htonl(nCRC32);
+ }
+
+ storeError verify() const
+ {
+ sal_uInt32 nMagic = store::ntohl(m_aGuard.m_nMagic);
+ if (nMagic != STORE_MAGIC_SUPERBLOCK)
+ return store_E_WrongFormat;
+
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
+ return store_E_InvalidChecksum;
+ else
+ return store_E_None;
+ }
+};
+
+}
+
+namespace store
+{
+
+struct SuperBlockPage
+{
+ typedef OStoreSuperBlock SuperBlock;
+
+ SuperBlock m_aSuperOne;
+ SuperBlock m_aSuperTwo;
+
+ static const size_t theSize = 2 * SuperBlock::theSize;
+ static const sal_uInt16 thePageSize = theSize;
+ static_assert(STORE_MINIMUM_PAGESIZE >= thePageSize, "must be at least thePageSize");
+
+ static void * operator new (size_t n)
+ {
+ return std::malloc(sal::static_int_cast<sal_Size>(n));
+ }
+
+ static void operator delete (void * p)
+ {
+ std::free (p);
+ }
+
+ static void * operator new (SAL_UNUSED_PARAMETER size_t, sal_uInt16 nPageSize)
+ {
+ return rtl_allocateZeroMemory (sal::static_int_cast<sal_Size>(nPageSize));
+ }
+
+ static void operator delete (void * p, SAL_UNUSED_PARAMETER sal_uInt16)
+ {
+ std::free (p);
+ }
+
+ explicit SuperBlockPage (sal_uInt16 nPageSize = thePageSize)
+ : m_aSuperOne(nPageSize),
+ m_aSuperTwo(nPageSize)
+ {}
+
+ storeError save (OStorePageBIOS const & rBIOS, sal_uInt32 nSize = theSize)
+ {
+ m_aSuperOne.guard();
+ m_aSuperTwo = m_aSuperOne;
+ return rBIOS.write (0, this, nSize);
+ }
+
+ /** Page allocation.
+ */
+ storeError unusedHead (
+ OStorePageBIOS const & rBIOS,
+ PageData & rPageHead);
+
+ storeError unusedPop (
+ OStorePageBIOS const & rBIOS,
+ PageData const & rPageHead);
+
+ storeError unusedPush (
+ OStorePageBIOS const & rBIOS,
+ sal_uInt32 nAddr);
+
+ storeError verify (OStorePageBIOS const & rBIOS);
+};
+
+} // namespace store
+
+/**
+ Get freelist head (alloc page, step 1).
+ */
+storeError SuperBlockPage::unusedHead (OStorePageBIOS const & 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();
+ if (nAddr == STORE_PAGE_NULL)
+ {
+ SAL_WARN("store", "store::SuperBlock::unusedHead(): page not free");
+
+ // Page in use.
+ rPageHead.location (STORE_PAGE_NULL);
+
+ // Recovery: Reset freelist to empty.
+ m_aSuperOne.unusedReset();
+ eErrCode = save (rBIOS);
+ }
+ return eErrCode;
+}
+
+/**
+ Pop freelist head (alloc page, step 2).
+ */
+storeError SuperBlockPage::unusedPop (OStorePageBIOS const & 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);
+}
+
+/**
+ Push new freelist head.
+ */
+storeError SuperBlockPage::unusedPush (OStorePageBIOS const & 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 const & 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.
+ SAL_WARN("store", "OStoreSuperBlockPage::verify(): double failure.");
+ }
+ }
+
+ // Done.
+ return eErrCode;
+}
+
+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, SAL_UNUSED_PARAMETER void*)
+{
+ 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;
+}
+
+namespace store
+{
+
+class OStorePageBIOS::AceCache
+{
+ rtl_cache_type * m_ace_cache;
+
+public:
+ static AceCache & get();
+
+ OStorePageBIOS::Ace *
+ create (sal_uInt32 addr);
+
+ void
+ destroy (OStorePageBIOS::Ace * ace);
+
+protected:
+ AceCache();
+ ~AceCache();
+};
+
+} // namespace store
+
+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
+ OStorePageBIOS::Ace::constructor,
+ nullptr, // destructor,
+ nullptr, // reclaim,
+ nullptr, // userarg,
+ nullptr, // default source,
+ 0 // flags
+ );
+}
+
+OStorePageBIOS::AceCache::~AceCache()
+{
+ rtl_cache_destroy (m_ace_cache);
+ m_ace_cache = nullptr;
+}
+
+OStorePageBIOS::Ace *
+OStorePageBIOS::AceCache::create (sal_uInt32 addr)
+{
+ Ace * ace = static_cast<Ace*>(rtl_cache_alloc (m_ace_cache));
+ if (ace != nullptr)
+ {
+ // verify invariant state.
+ OSL_ASSERT((ace->m_next == ace) && (ace->m_prev == ace));
+
+ // initialize.
+ ace->m_addr = addr;
+ ace->m_used = 1;
+ }
+ return ace;
+}
+
+void
+OStorePageBIOS::AceCache::destroy (OStorePageBIOS::Ace * ace)
+{
+ if (ace != nullptr)
+ {
+ // 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::OStorePageBIOS()
+ : m_bWriteable (false)
+{
+}
+
+OStorePageBIOS::~OStorePageBIOS()
+{
+ cleanup_Impl();
+}
+
+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.
+ @pre 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 != storeAccessMode::ReadOnly);
+
+ // Check access mode.
+ storeError eErrCode = store_E_None;
+ if (eAccessMode != storeAccessMode::Create)
+ {
+ // Load SuperBlock page.
+ m_pSuper.reset(new SuperBlockPage());
+
+ eErrCode = read (0, m_pSuper.get(), 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 == storeAccessMode::ReadOnly)
+ return store_E_NotExists;
+ if (eAccessMode == storeAccessMode::ReadWrite)
+ 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).
+ m_pSuper.reset(new(rnPageSize) SuperBlockPage(rnPageSize));
+ 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;
+}
+
+/*
+ @pre exclusive access.
+ */
+void OStorePageBIOS::cleanup_Impl()
+{
+ // Check referer count.
+ if (m_ace_head.m_used > 0)
+ {
+ // Report remaining referer count.
+ SAL_INFO("store", "referer count: " << 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.
+ m_pSuper.reset();
+
+ // Release PageCache.
+ m_xCache.clear();
+
+ // Release PageAllocator.
+ m_xAllocator.clear();
+
+ // Release LockBytes.
+ m_xLockBytes.clear();
+}
+
+/**
+ @pre initialized, exclusive access.
+ */
+storeError OStorePageBIOS::read (
+ sal_uInt32 nAddr, void *pData, sal_uInt32 nSize) const
+{
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+
+ // Read Data.
+ return m_xLockBytes->readAt (nAddr, pData, nSize);
+}
+
+/**
+ @pre initialized, writeable, exclusive access.
+ */
+storeError OStorePageBIOS::write (
+ sal_uInt32 nAddr, const void *pData, sal_uInt32 nSize) const
+{
+ // 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);
+}
+
+/**
+ @pre 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 == storeAccessMode::ReadOnly)))
+ 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 == storeAccessMode::ReadOnly)
+ ace->m_used += 1;
+ else
+ return store_E_AccessViolation;
+ }
+ else
+ {
+ // Insert new entry.
+ Ace * entry = AceCache::get().create (rDescr.m_nAddr);
+ 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;
+}
+
+/**
+ @pre initialized.
+ */
+storeError OStorePageBIOS::releasePage (const OStorePageDescriptor& rDescr)
+{
+ // 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;
+}
+
+/**
+ @pre initialized, writeable.
+ */
+storeError OStorePageBIOS::allocate (
+ OStorePageObject& rPage)
+{
+ // 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;
+ // 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);
+}
+
+/**
+ @pre 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);
+}
+
+/**
+ @pre 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);
+}
+
+/**
+ @pre initialized, readable, exclusive access.
+ */
+storeError OStorePageBIOS::loadObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr) const
+{
+ 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);
+}
+
+/**
+ @pre 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);
+}
+
+/**
+ @pre initialized, writeable, exclusive access.
+ */
+storeError OStorePageBIOS::saveObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr) const
+{
+ // 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);
+}
+
+/**
+ @pre none.
+ */
+storeError OStorePageBIOS::close()
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Cleanup.
+ cleanup_Impl();
+
+ // Done.
+ return store_E_None;
+}
+
+/**
+ @pre initialized.
+ */
+storeError OStorePageBIOS::flush()
+{
+ // 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();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storbios.hxx b/store/source/storbios.hxx
new file mode 100644
index 0000000000..5eb60bc5b8
--- /dev/null
+++ b/store/source/storbios.hxx
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ref.hxx>
+#include <osl/mutex.hxx>
+
+#include <store/types.h>
+#include "object.hxx"
+#include "storbase.hxx"
+
+namespace store
+{
+
+class ILockBytes;
+class PageCache;
+struct SuperBlockPage;
+
+class OStorePageBIOS : public store::OStoreObject
+{
+public:
+ OStorePageBIOS();
+
+ /** Conversion into Mutex&
+ */
+ inline operator osl::Mutex& (void) const;
+
+ /** Initialization.
+ * @param pLockBytes [in]
+ * @param eAccessMode [in]
+ * @param rnPageSize [inout]
+ * @return store_E_None upon success
+ */
+ virtual storeError initialize (
+ ILockBytes * pLockBytes,
+ storeAccessMode eAccessMode,
+ sal_uInt16 & rnPageSize);
+
+ rtl::Reference< PageData::Allocator > & allocator()
+ {
+ return m_xAllocator;
+ }
+
+ storeError read (
+ sal_uInt32 nAddr, void *pData, sal_uInt32 nSize) const;
+
+ storeError write (
+ sal_uInt32 nAddr, const void *pData, sal_uInt32 nSize) const;
+
+ inline bool isWriteable() const;
+
+ inline bool isValid() const;
+
+ storeError acquirePage (
+ const OStorePageDescriptor& rDescr, storeAccessMode eMode);
+
+ storeError releasePage (const OStorePageDescriptor& rDescr);
+
+ storeError allocate (OStorePageObject& rPage);
+
+ storeError free (sal_uInt32 nAddr);
+
+ /** Page I/O.
+ */
+ storeError loadObjectAt (
+ OStorePageObject& rPage, sal_uInt32 nAddr);
+
+ storeError saveObjectAt (
+ OStorePageObject& rPage, sal_uInt32 nAddr);
+
+ /** close.
+ @return store_E_None upon success.
+ */
+ storeError close();
+
+ /** flush.
+ @return store_E_None upon success.
+ */
+ storeError flush();
+
+protected:
+ virtual ~OStorePageBIOS() override;
+
+private:
+ rtl::Reference<ILockBytes> m_xLockBytes;
+ osl::Mutex m_aMutex;
+
+ std::unique_ptr<SuperBlockPage> m_pSuper;
+
+ bool m_bWriteable;
+
+ rtl::Reference< PageData::Allocator > m_xAllocator;
+ rtl::Reference< PageCache > m_xCache;
+
+public:
+ /** Page Access (control).
+ */
+ struct Ace
+ {
+ Ace * m_next;
+ Ace * m_prev;
+
+ sal_uInt32 m_addr;
+ sal_uInt32 m_used;
+
+ Ace();
+ ~Ace();
+
+ static int SAL_CALL constructor (void * obj, void * arg);
+
+ static Ace * find (Ace * head, sal_uInt32 addr);
+ static void insert (Ace * head, Ace * entry);
+ };
+
+private:
+ Ace m_ace_head;
+
+ class AceCache;
+
+ storeError initialize_Impl (
+ ILockBytes * pLockBytes,
+ storeAccessMode eAccessMode,
+ sal_uInt16 & rnPageSize);
+ void cleanup_Impl();
+
+ storeError loadObjectAt_Impl (
+ OStorePageObject & rPage, sal_uInt32 nAddr) const;
+ storeError saveObjectAt_Impl (
+ OStorePageObject & rPage, sal_uInt32 nAddr) const;
+
+ OStorePageBIOS (const OStorePageBIOS&) = delete;
+ OStorePageBIOS& operator= (const OStorePageBIOS&) = delete;
+};
+
+inline OStorePageBIOS::operator osl::Mutex& (void) const
+{
+ return const_cast<osl::Mutex&>(m_aMutex);
+}
+
+inline bool OStorePageBIOS::isWriteable() const
+{
+ return m_bWriteable;
+}
+
+inline bool OStorePageBIOS::isValid() const
+{
+ return m_xLockBytes.is();
+}
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storcach.cxx b/store/source/storcach.cxx
new file mode 100644
index 0000000000..bb4e34b5af
--- /dev/null
+++ b/store/source/storcach.cxx
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include "storcach.hxx"
+
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <sal/macros.h>
+#include <rtl/alloc.h>
+#include <osl/diagnose.h>
+
+#include <store/types.h>
+#include "storbase.hxx"
+
+#include <memory>
+#include <string.h>
+
+using namespace store;
+
+namespace store {
+struct Entry
+{
+ std::shared_ptr<PageData> m_xPage;
+ sal_uInt32 m_nOffset;
+ Entry * m_pNext;
+
+ static void * operator new (size_t, void * p) { return p; }
+ static void operator delete (void *, void *) {}
+
+ explicit Entry (std::shared_ptr<PageData> const & rxPage, sal_uInt32 nOffset)
+ : m_xPage(rxPage), m_nOffset(nOffset), m_pNext(nullptr)
+ {}
+};
+};
+
+namespace
+{
+
+class EntryCache
+{
+ rtl_cache_type * m_entry_cache;
+
+public:
+ static EntryCache & get();
+
+ Entry * create (std::shared_ptr<PageData> const & rxPage, sal_uInt32 nOffset);
+
+ void destroy (Entry * entry);
+
+protected:
+ EntryCache();
+ ~EntryCache();
+};
+
+} // namespace
+
+EntryCache & EntryCache::get()
+{
+ static EntryCache g_entry_cache;
+ return g_entry_cache;
+}
+
+EntryCache::EntryCache()
+{
+ m_entry_cache = rtl_cache_create (
+ "store_cache_entry_cache",
+ sizeof(Entry),
+ 0, // objalign
+ nullptr, // constructor
+ nullptr, // destructor
+ nullptr, // reclaim
+ nullptr, // userarg
+ nullptr, // default source
+ 0 // flags
+ );
+}
+
+EntryCache::~EntryCache()
+{
+ rtl_cache_destroy (m_entry_cache);
+ m_entry_cache = nullptr;
+}
+
+Entry * EntryCache::create (std::shared_ptr<PageData> const & rxPage, sal_uInt32 nOffset)
+{
+ void * pAddr = rtl_cache_alloc (m_entry_cache);
+ if (pAddr != nullptr)
+ {
+ // construct
+ return new(pAddr) Entry (rxPage, nOffset);
+ }
+ return nullptr;
+}
+
+void EntryCache::destroy (Entry * entry)
+{
+ if (entry != nullptr)
+ {
+ // destruct
+ entry->~Entry();
+
+ // return to cache
+ rtl_cache_free (m_entry_cache, entry);
+ }
+}
+
+// highbit():= log2() + 1 (complexity O(1))
+static int highbit(std::size_t n)
+{
+ int k = 1;
+
+ if (n == 0)
+ return 0;
+ if constexpr (sizeof(n) == 8)
+ {
+ if (n & 0xffffffff00000000)
+ {
+ k |= 32;
+ n >>= 32;
+ }
+ }
+ if (n & 0xffff0000)
+ {
+ k |= 16;
+ n >>= 16;
+ }
+ if (n & 0xff00)
+ {
+ k |= 8;
+ n >>= 8;
+ }
+ if (n & 0xf0)
+ {
+ k |= 4;
+ n >>= 4;
+ }
+ if (n & 0x0c)
+ {
+ k |= 2;
+ n >>= 2;
+ }
+ if (n & 0x02)
+ k++;
+
+ return k;
+}
+
+
+PageCache::PageCache (sal_uInt16 nPageSize)
+ : m_hash_table (m_hash_table_0),
+ m_hash_size (theTableSize),
+ m_hash_shift (highbit(m_hash_size) - 1),
+ m_page_shift (highbit(nPageSize) - 1),
+ m_hash_entries (0),
+ m_nHit (0),
+ m_nMissed (0)
+{
+ static size_t const theSize = SAL_N_ELEMENTS(m_hash_table_0);
+ static_assert(theSize == theTableSize, "must be equal");
+}
+
+PageCache::~PageCache()
+{
+ double s_x = 0.0;
+ std::size_t i, n = m_hash_size;
+ for (i = 0; i < n; i++)
+ {
+ int x = 0;
+ Entry * entry = m_hash_table[i];
+ while (entry != nullptr)
+ {
+ m_hash_table[i] = entry->m_pNext;
+ entry->m_pNext = nullptr;
+ EntryCache::get().destroy (entry);
+ entry = m_hash_table[i];
+ x += 1;
+ }
+ s_x += double(x);
+ }
+ double ave = s_x / double(n);
+ SAL_INFO("store", "avg hash chain length: " << ave);
+
+ if (m_hash_table != m_hash_table_0)
+ {
+ std::free (m_hash_table);
+ m_hash_table = m_hash_table_0;
+ m_hash_size = theTableSize;
+ m_hash_shift = highbit(m_hash_size) - 1;
+ }
+ SAL_INFO("store", "Hits: " << m_nHit << ", Misses: " << m_nMissed);
+}
+
+void PageCache::rescale_Impl (std::size_t new_size)
+{
+ std::size_t new_bytes = new_size * sizeof(Entry*);
+ Entry ** new_table = static_cast<Entry**>(std::malloc(new_bytes));
+
+ if (new_table == nullptr)
+ return;
+
+ Entry ** old_table = m_hash_table;
+ std::size_t old_size = m_hash_size;
+
+ SAL_INFO(
+ "store",
+ "ave chain length: " << (m_hash_entries >> m_hash_shift)
+ << ", total entries: " << m_hash_entries << " [old_size: "
+ << old_size << " new_size: " << new_size << "]");
+
+ memset (new_table, 0, new_bytes);
+
+ m_hash_table = new_table;
+ m_hash_size = new_size;
+ m_hash_shift = highbit(m_hash_size) - 1;
+
+ std::size_t i;
+ for (i = 0; i < old_size; i++)
+ {
+ Entry * curr = old_table[i];
+ while (curr != nullptr)
+ {
+ Entry * next = curr->m_pNext;
+ int index = hash_index_Impl(curr->m_nOffset);
+ curr->m_pNext = m_hash_table[index];
+ m_hash_table[index] = curr;
+ curr = next;
+ }
+ old_table[i] = nullptr;
+ }
+ if (old_table != m_hash_table_0)
+ {
+
+ std::free (old_table);
+ }
+}
+
+Entry * PageCache::lookup_Impl (Entry * entry, sal_uInt32 nOffset)
+{
+ int lookups = 0;
+ while (entry != nullptr)
+ {
+ if (entry->m_nOffset == nOffset)
+ break;
+
+ lookups += 1;
+ entry = entry->m_pNext;
+ }
+ if (lookups > 2)
+ {
+ std::size_t new_size = m_hash_size, ave = m_hash_entries >> m_hash_shift;
+ for (; ave > 4; new_size *= 2, ave /= 2)
+ continue;
+ if (new_size != m_hash_size)
+ rescale_Impl (new_size);
+ }
+ return entry;
+}
+
+storeError PageCache::lookupPageAt (std::shared_ptr<PageData> & rxPage, sal_uInt32 nOffset)
+{
+ OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::PageCache::lookupPageAt(): invalid Offset");
+ if (nOffset == STORE_PAGE_NULL)
+ return store_E_CantSeek;
+
+ int index = hash_index_Impl(nOffset);
+ Entry const * entry = lookup_Impl (m_hash_table[index], nOffset);
+ if (entry != nullptr)
+ {
+ // Existing entry.
+ rxPage = entry->m_xPage;
+
+ // Update stats and leave.
+ m_nHit += 1;
+ return store_E_None;
+ }
+
+ // Cache miss. Update stats and leave.
+ m_nMissed += 1;
+ return store_E_NotExists;
+}
+
+storeError PageCache::insertPageAt (std::shared_ptr<PageData> const & rxPage, sal_uInt32 nOffset)
+{
+ // [SECURITY:ValInput]
+ PageData const * pagedata = rxPage.get();
+ OSL_PRECOND(!(pagedata == nullptr), "store::PageCache::insertPageAt(): invalid Page");
+ if (pagedata == nullptr)
+ return store_E_InvalidParameter;
+
+ sal_uInt32 const offset = pagedata->location();
+ OSL_PRECOND(!(nOffset != offset), "store::PageCache::insertPageAt(): inconsistent Offset");
+ if (nOffset != offset)
+ return store_E_InvalidParameter;
+
+ OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::PageCache::insertPageAt(): invalid Offset");
+ if (nOffset == STORE_PAGE_NULL)
+ return store_E_CantSeek;
+
+ Entry * entry = EntryCache::get().create (rxPage, nOffset);
+ if (entry != nullptr)
+ {
+ // Insert new entry.
+ int index = hash_index_Impl(nOffset);
+ entry->m_pNext = m_hash_table[index];
+ m_hash_table[index] = entry;
+
+ // Update stats and leave.
+ m_hash_entries += 1;
+ return store_E_None;
+ }
+ return store_E_OutOfMemory;
+}
+
+storeError PageCache::updatePageAt (std::shared_ptr<PageData> const & rxPage, sal_uInt32 nOffset)
+{
+ // [SECURITY:ValInput]
+ PageData const * pagedata = rxPage.get();
+ OSL_PRECOND(!(pagedata == nullptr), "store::PageCache::updatePageAt(): invalid Page");
+ if (pagedata == nullptr)
+ return store_E_InvalidParameter;
+
+ sal_uInt32 const offset = pagedata->location();
+ OSL_PRECOND(!(nOffset != offset), "store::PageCache::updatePageAt(): inconsistent Offset");
+ if (nOffset != offset)
+ return store_E_InvalidParameter;
+
+ OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::PageCache::updatePageAt(): invalid Offset");
+ if (nOffset == STORE_PAGE_NULL)
+ return store_E_CantSeek;
+
+ int index = hash_index_Impl(nOffset);
+ Entry * entry = lookup_Impl (m_hash_table[index], nOffset);
+ if (entry != nullptr)
+ {
+ // Update existing entry.
+ entry->m_xPage = rxPage;
+
+ // Update stats and leave. // m_nUpdHit += 1;
+ return store_E_None;
+ }
+ return insertPageAt (rxPage, nOffset);
+}
+
+storeError PageCache::removePageAt (sal_uInt32 nOffset)
+{
+ OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::PageCache::removePageAt(): invalid Offset");
+ if (nOffset == STORE_PAGE_NULL)
+ return store_E_CantSeek;
+
+ Entry ** ppEntry = &(m_hash_table[hash_index_Impl(nOffset)]);
+ while (*ppEntry != nullptr)
+ {
+ if ((*ppEntry)->m_nOffset == nOffset)
+ {
+ // Existing entry.
+ Entry * entry = *ppEntry;
+
+ // Dequeue and destroy entry.
+ (*ppEntry) = entry->m_pNext;
+ entry->m_pNext = nullptr;
+ EntryCache::get().destroy (entry);
+
+ // Update stats and leave.
+ m_hash_entries -= 1;
+ return store_E_None;
+ }
+ ppEntry = &((*ppEntry)->m_pNext);
+ }
+ return store_E_NotExists;
+}
+
+/**
+
+ Old OStorePageCache implementation.
+
+ (two-way association (sorted address array, LRU chain)).
+ (external PageData representation).
+ */
+
+namespace store {
+
+storeError
+PageCache_createInstance (
+ rtl::Reference< store::PageCache > & rxCache,
+ sal_uInt16 nPageSize)
+{
+ rxCache = new PageCache (nPageSize);
+ if (!rxCache.is())
+ return store_E_OutOfMemory;
+
+ return store_E_None;
+}
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storcach.hxx b/store/source/storcach.hxx
new file mode 100644
index 0000000000..b787248349
--- /dev/null
+++ b/store/source/storcach.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <sal/types.h>
+
+#include <store/types.h>
+#include "object.hxx"
+
+namespace rtl { template <class reference_type> class Reference; }
+namespace store { struct PageData; }
+
+namespace store
+{
+
+struct Entry;
+
+class PageCache :
+ public store::OStoreObject
+{
+ static size_t const theTableSize = 32;
+ static_assert((theTableSize & (theTableSize-1)) == 0, "table size should be a power of 2");
+
+ Entry ** m_hash_table;
+ Entry * m_hash_table_0[theTableSize] = {};
+ size_t m_hash_size;
+ size_t m_hash_shift;
+ size_t const m_page_shift;
+
+ size_t m_hash_entries; // total number of entries in table.
+ size_t m_nHit;
+ size_t m_nMissed;
+
+ static int hash_Impl(sal_uInt32 a, size_t s, size_t q, size_t m)
+ {
+ return static_cast<int>(((a + (a >> s) + (a >> (s << 1))) >> q) & m);
+ }
+ int hash_index_Impl (sal_uInt32 nOffset)
+ {
+ return hash_Impl(nOffset, m_hash_shift, m_page_shift, m_hash_size - 1);
+ }
+
+ Entry * lookup_Impl (Entry * entry, sal_uInt32 nOffset);
+ void rescale_Impl (std::size_t new_size);
+
+public:
+ explicit PageCache (sal_uInt16 nPageSize);
+
+ PageCache(const PageCache&) = delete;
+ PageCache& operator=(const PageCache&) = delete;
+
+ storeError lookupPageAt (
+ std::shared_ptr<PageData> & rxPage,
+ sal_uInt32 nOffset);
+
+ storeError insertPageAt (
+ std::shared_ptr<PageData> const & rxPage,
+ sal_uInt32 nOffset);
+
+ storeError updatePageAt (
+ std::shared_ptr<PageData> const & rxPage,
+ sal_uInt32 nOffset);
+
+ storeError removePageAt (
+ sal_uInt32 nOffset);
+
+protected:
+ virtual ~PageCache() override;
+};
+
+storeError PageCache_createInstance (
+ rtl::Reference< store::PageCache > & rxCache,
+ sal_uInt16 nPageSize
+);
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/stordata.cxx b/store/source/stordata.cxx
new file mode 100644
index 0000000000..5eaf531aed
--- /dev/null
+++ b/store/source/stordata.cxx
@@ -0,0 +1,1043 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "stordata.hxx"
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+
+#include <store/types.h>
+#include "storbase.hxx"
+#include "storbios.hxx"
+
+using namespace store;
+
+storeError OStoreDataPageObject::guard (sal_uInt32 nAddr)
+{
+ return PageHolderObject< page >::guard (m_xPage, nAddr);
+}
+
+storeError OStoreDataPageObject::verify (sal_uInt32 nAddr) const
+{
+ return PageHolderObject< page >::verify (m_xPage, nAddr);
+}
+
+/**
+ 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;
+}
+
+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);
+}
+
+storeError OStoreIndirectionPageObject::guard (sal_uInt32 nAddr)
+{
+ return PageHolderObject< page >::guard (m_xPage, nAddr);
+}
+
+storeError OStoreIndirectionPageObject::verify (sal_uInt32 nAddr) const
+{
+ return PageHolderObject< page >::verify (m_xPage, nAddr);
+}
+
+storeError OStoreIndirectionPageObject::read (
+ sal_uInt16 nSingle,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS) const
+{
+ 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) const
+{
+ 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) const
+{
+ 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;
+}
+
+storeError OStoreDirectoryPageObject::guard (sal_uInt32 nAddr)
+{
+ return PageHolderObject< page >::guard (m_xPage, nAddr);
+}
+
+storeError OStoreDirectoryPageObject::verify (sal_uInt32 nAddr) const
+{
+ return PageHolderObject< page >::verify (m_xPage, nAddr);
+}
+
+OStoreDirectoryPageData::ChunkScope
+OStoreDirectoryPageObject::scope (
+ sal_uInt32 nPage,
+ page::DataBlock::LinkDescriptor &rDescr) const
+{
+ page const & rPage = PAGE();
+
+ sal_uInt32 index0, index1, index2;
+
+ // direct.
+ sal_uInt32 nCount = OStoreDirectoryDataBlock::directCount;
+ sal_uInt32 nLimit = nCount;
+ if (nPage < nLimit)
+ {
+ // Page to index reduction.
+ index0 = nPage;
+
+ // Setup LinkDescriptor indices.
+ rDescr.m_nIndex0 = static_cast<sal_uInt16>(index0 & 0xffff);
+
+ // Done.
+ return page::SCOPE_DIRECT;
+ }
+ nPage -= nLimit;
+
+ // single indirect.
+ sal_uInt32 const nCapacity = indirect::capacityCount(rPage.m_aDescr);
+ nCount = OStoreDirectoryDataBlock::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;
+ if (n != nPage)
+ {
+ SAL_WARN("store", "wrong math on indirect indices");
+ return page::SCOPE_UNKNOWN;
+ }
+
+ // Setup LinkDescriptor indices.
+ rDescr.m_nIndex0 = static_cast<sal_uInt16>(index0 & 0xffff);
+ rDescr.m_nIndex1 = static_cast<sal_uInt16>(index1 & 0xffff);
+
+ // Done.
+ return page::SCOPE_SINGLE;
+ }
+ nPage -= nLimit;
+
+ // double indirect.
+ nCount = OStoreDirectoryDataBlock::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;
+ if (n != nPage)
+ {
+ SAL_WARN("store", "wrong math on double indirect indices");
+ return page::SCOPE_UNKNOWN;
+ }
+
+ // Setup LinkDescriptor indices.
+ rDescr.m_nIndex0 = static_cast<sal_uInt16>(index0 & 0xffff);
+ rDescr.m_nIndex1 = static_cast<sal_uInt16>(index1 & 0xffff);
+ rDescr.m_nIndex2 = static_cast<sal_uInt16>(index2 & 0xffff);
+
+ // Done.
+ return page::SCOPE_DOUBLE;
+ }
+ nPage -= nLimit;
+
+ // triple indirect.
+ nCount = OStoreDirectoryDataBlock::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.
+ sal_uInt32 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;
+ if (n != nPage)
+ {
+ SAL_WARN("store", "wrong math on triple indirect indices");
+ return page::SCOPE_UNKNOWN;
+ }
+
+ // Setup LinkDescriptor indices.
+ rDescr.m_nIndex0 = static_cast<sal_uInt16>(index0 & 0xffff);
+ rDescr.m_nIndex1 = static_cast<sal_uInt16>(index1 & 0xffff);
+ rDescr.m_nIndex2 = static_cast<sal_uInt16>(index2 & 0xffff);
+ rDescr.m_nIndex3 = static_cast<sal_uInt16>(index3 & 0xffff);
+
+ // Done.
+ return page::SCOPE_TRIPLE;
+ }
+
+ // Unreachable (more than triple indirect).
+ return page::SCOPE_UNREACHABLE;
+}
+
+storeError OStoreDirectoryPageObject::read (
+ sal_uInt32 nPage,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS) const
+{
+ // 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.
+ SAL_WARN("store", "OStoreDirectoryPageObject::get(): scope failed");
+ eErrCode = store_E_Unknown;
+ }
+
+ // Leave.
+ return eErrCode;
+}
+
+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.
+ SAL_WARN("store", "OStoreDirectoryPageObject::put(): scope failed");
+ eErrCode = store_E_Unknown;
+ }
+
+ // Leave.
+ return eErrCode;
+}
+
+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.
+ SAL_WARN("store", "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)
+{
+ // Enter.
+ storeError eErrCode = store_E_None;
+ if (eScope == page::SCOPE_DIRECT)
+ {
+ // Truncate direct data pages.
+ for (sal_uInt16 i = OStoreDirectoryDataBlock::directCount; 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.
+ for (sal_uInt16 i = OStoreDirectoryDataBlock::singleCount; 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.
+ for (sal_uInt16 i = OStoreDirectoryDataBlock::doubleCount; 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.
+ for (sal_uInt16 i = OStoreDirectoryDataBlock::tripleCount; 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: */
diff --git a/store/source/stordata.hxx b/store/source/stordata.hxx
new file mode 100644
index 0000000000..7b5b8a0b84
--- /dev/null
+++ b/store/source/stordata.hxx
@@ -0,0 +1,748 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <sal/types.h>
+#include <rtl/string.h>
+
+#include <store/types.h>
+#include "storbase.hxx"
+#include <string.h>
+
+namespace store
+{
+
+class OStorePageBIOS;
+
+constexpr sal_uInt32 STORE_MAGIC_DATAPAGE(0x94190310);
+
+struct OStoreDataPageData : public store::PageData
+{
+ typedef PageData base;
+ typedef OStoreDataPageData self;
+
+ typedef OStorePageDescriptor D;
+
+ /** Representation.
+ */
+ sal_uInt8 m_pData[1];
+
+ /** type.
+ */
+ static const sal_uInt32 theTypeId = STORE_MAGIC_DATAPAGE;
+
+ /** size.
+ */
+ static const size_t theSize = 0;
+ static const sal_uInt16 thePageSize = base::theSize + self::theSize;
+ static_assert(STORE_MINIMUM_PAGESIZE >= self::thePageSize, "got to be at least equal in size");
+
+ /** capacity.
+ */
+ static sal_uInt16 capacity (const D& rDescr) // @see inode::ChunkDescriptor
+ {
+ return static_cast<sal_uInt16>(store::ntohs(rDescr.m_nSize) - self::thePageSize);
+ }
+ sal_uInt16 capacity() const
+ {
+ return self::capacity (base::m_aDescr);
+ }
+
+ /** Construction.
+ */
+ explicit OStoreDataPageData (sal_uInt16 nPageSize)
+ : base (nPageSize)
+ {
+ base::m_aGuard.m_nMagic = store::htonl(self::theTypeId);
+ base::m_aDescr.m_nUsed = store::htons(self::thePageSize);
+ if (capacity()) memset (m_pData, 0, capacity());
+ }
+
+ /** guard (external representation).
+ */
+ void guard() const { (void) this; /* loplugin:staticmethods */ }
+
+ /** verify (external representation).
+ */
+ storeError verify() const {
+ (void) this; // loplugin:staticmethods
+ return store_E_None;
+ }
+};
+
+class OStoreDataPageObject : public store::OStorePageObject
+{
+ typedef OStorePageObject base;
+ typedef OStoreDataPageData page;
+
+public:
+ /** Construction.
+ */
+ explicit OStoreDataPageObject (std::shared_ptr<PageData> const & rxPage = std::shared_ptr<PageData>())
+ : OStorePageObject (rxPage)
+ {}
+
+ /** External representation.
+ */
+ virtual storeError guard (sal_uInt32 nAddr) override;
+ virtual storeError verify (sal_uInt32 nAddr) const override;
+};
+
+constexpr sal_uInt32 STORE_MAGIC_INDIRECTPAGE(0x89191107);
+
+struct OStoreIndirectionPageData : public store::PageData
+{
+ typedef PageData base;
+ typedef OStoreIndirectionPageData self;
+
+ typedef OStorePageGuard G;
+ typedef OStorePageDescriptor D;
+
+ /** Representation.
+ */
+ G m_aGuard;
+ sal_uInt32 m_pData[1];
+
+ /** type.
+ */
+ static const sal_uInt32 theTypeId = STORE_MAGIC_INDIRECTPAGE;
+
+ /** size.
+ */
+ static const size_t theSize = sizeof(G);
+ static const sal_uInt16 thePageSize = base::theSize + self::theSize;
+ static_assert(STORE_MINIMUM_PAGESIZE >= self::thePageSize, "got to be at least equal in size");
+
+ /** capacity.
+ */
+ static sal_uInt16 capacity (const D& rDescr)
+ {
+ return static_cast<sal_uInt16>(store::ntohs(rDescr.m_nSize) - self::thePageSize);
+ }
+ sal_uInt16 capacity() const
+ {
+ return self::capacity (base::m_aDescr);
+ }
+
+ /** capacityCount.
+ */
+ static sal_uInt16 capacityCount (const D& rDescr) // @see DirectoryPageObject::scope()
+ {
+ return sal_uInt16(capacity(rDescr) / sizeof(sal_uInt32));
+ }
+ sal_uInt16 capacityCount() const
+ {
+ return sal_uInt16(capacity() / sizeof(sal_uInt32));
+ }
+
+ /** Construction.
+ */
+ explicit OStoreIndirectionPageData (sal_uInt16 nPageSize)
+ : base (nPageSize)
+ {
+ base::m_aGuard.m_nMagic = store::htonl(self::theTypeId);
+ base::m_aDescr.m_nUsed = store::htons(self::thePageSize);
+ self::m_aGuard.m_nMagic = store::htonl(0);
+ memset (m_pData, STORE_PAGE_NULL, capacity());
+ }
+
+ /** guard (external representation).
+ */
+ void guard()
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, m_pData, capacity());
+ m_aGuard.m_nCRC32 = store::htonl(nCRC32);
+ }
+
+ /** verify (external representation).
+ */
+ storeError verify() const
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, m_pData, capacity());
+ if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
+ return store_E_InvalidChecksum;
+ else
+ return store_E_None;
+ }
+};
+
+class OStoreIndirectionPageObject : public store::OStorePageObject
+{
+ typedef OStorePageObject base;
+ typedef OStoreIndirectionPageData page;
+
+public:
+ /** Construction.
+ */
+ explicit OStoreIndirectionPageObject (std::shared_ptr<PageData> const & rxPage = std::shared_ptr<PageData>())
+ : OStorePageObject (rxPage)
+ {}
+
+ /** External representation.
+ */
+ storeError loadOrCreate (
+ sal_uInt32 nAddr,
+ OStorePageBIOS & rBIOS);
+
+ virtual storeError guard (sal_uInt32 nAddr) override;
+ virtual storeError verify (sal_uInt32 nAddr) const override;
+
+ /** read (indirect data page).
+ */
+ storeError read (
+ sal_uInt16 nSingle,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS) const;
+
+ storeError read (
+ sal_uInt16 nDouble,
+ sal_uInt16 nSingle,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS) const;
+
+ storeError read (
+ sal_uInt16 nTriple,
+ sal_uInt16 nDouble,
+ sal_uInt16 nSingle,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS) const;
+
+ /** write (indirect data page).
+ */
+ storeError write (
+ sal_uInt16 nSingle,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS);
+
+ storeError write (
+ sal_uInt16 nDouble,
+ sal_uInt16 nSingle,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS);
+
+ storeError write (
+ sal_uInt16 nTriple,
+ sal_uInt16 nDouble,
+ sal_uInt16 nSingle,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS);
+
+ /** truncate (indirect data page).
+ */
+ storeError truncate (
+ sal_uInt16 nSingle,
+ OStorePageBIOS &rBIOS);
+
+ storeError truncate (
+ sal_uInt16 nDouble,
+ sal_uInt16 nSingle,
+ OStorePageBIOS &rBIOS);
+
+ storeError truncate (
+ sal_uInt16 nTriple,
+ sal_uInt16 nDouble,
+ sal_uInt16 nSingle,
+ OStorePageBIOS &rBIOS);
+};
+
+struct OStorePageNameBlock
+{
+ typedef OStorePageGuard G;
+ typedef OStorePageKey K;
+
+ /** Representation.
+ */
+ G m_aGuard;
+ K m_aKey;
+ sal_uInt32 m_nAttrib = 0;
+ char m_pData[STORE_MAXIMUM_NAMESIZE] = {};
+
+ /** size.
+ */
+ static const size_t theSize = sizeof(G) + sizeof(K) + sizeof(sal_uInt32) + sizeof(char[STORE_MAXIMUM_NAMESIZE]);
+
+ /** Construction.
+ */
+ OStorePageNameBlock() = default;
+
+ /** guard (external representation).
+ */
+ void guard()
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, &m_aKey, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ m_aGuard.m_nCRC32 = store::htonl(nCRC32);
+ }
+
+ /** verify (external representation).
+ */
+ storeError verify() const
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, &m_aKey, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
+ return store_E_InvalidChecksum;
+ else
+ return store_E_None;
+ }
+};
+
+#define STORE_LIMIT_DATAPAGE_DIRECT 16
+#define STORE_LIMIT_DATAPAGE_SINGLE 8
+#define STORE_LIMIT_DATAPAGE_DOUBLE 1
+#define STORE_LIMIT_DATAPAGE_TRIPLE 1
+
+struct OStoreDirectoryDataBlock
+{
+ typedef OStorePageGuard G;
+
+ /** LinkDescriptor.
+ */
+ struct LinkDescriptor
+ {
+ /** Representation.
+ */
+ sal_uInt16 m_nIndex0;
+ sal_uInt16 m_nIndex1;
+ sal_uInt16 m_nIndex2;
+ sal_uInt16 m_nIndex3;
+
+ /** Construction.
+ */
+ LinkDescriptor()
+ : m_nIndex0 (sal_uInt16(~0)),
+ m_nIndex1 (sal_uInt16(~0)),
+ m_nIndex2 (sal_uInt16(~0)),
+ m_nIndex3 (sal_uInt16(~0))
+ {}
+ };
+
+ /** LinkTable.
+ */
+ struct LinkTable
+ {
+ /** Representation.
+ */
+ sal_uInt32 m_pDirect[STORE_LIMIT_DATAPAGE_DIRECT];
+ sal_uInt32 m_pSingle[STORE_LIMIT_DATAPAGE_SINGLE];
+ sal_uInt32 m_pDouble[STORE_LIMIT_DATAPAGE_DOUBLE];
+ sal_uInt32 m_pTriple[STORE_LIMIT_DATAPAGE_TRIPLE];
+
+ /** initialize.
+ */
+ void initialize()
+ {
+ memset(m_pDirect, STORE_PAGE_NULL, sizeof(m_pDirect));
+ memset(m_pSingle, STORE_PAGE_NULL, sizeof(m_pSingle));
+ memset(m_pDouble, STORE_PAGE_NULL, sizeof(m_pDouble));
+ memset(m_pTriple, STORE_PAGE_NULL, sizeof(m_pTriple));
+ }
+
+ /** Construction.
+ */
+ LinkTable()
+ {
+ initialize();
+ }
+ };
+
+ /** Representation.
+ */
+ G m_aGuard;
+ LinkTable m_aTable;
+ sal_uInt32 m_nDataLen;
+
+ /** size.
+ */
+ static const size_t theSize = sizeof(G) + sizeof(LinkTable) + sizeof(sal_uInt32);
+
+ /** Construction.
+ */
+ OStoreDirectoryDataBlock()
+ : m_aGuard(), m_aTable(), m_nDataLen (0)
+ {}
+
+ /** guard (external representation).
+ */
+ void guard()
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, &m_aTable, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ m_aGuard.m_nCRC32 = store::htonl(nCRC32);
+ }
+
+ /** verify (external representation).
+ */
+ storeError verify() const
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, &m_aTable, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
+ return store_E_InvalidChecksum;
+ else
+ return store_E_None;
+ }
+
+ /** direct.
+ */
+ static const sal_uInt16 directCount = sal_uInt16(STORE_LIMIT_DATAPAGE_DIRECT);
+
+ sal_uInt32 directLink (sal_uInt16 nIndex) const
+ {
+ if (nIndex < directCount)
+ return store::ntohl(m_aTable.m_pDirect[nIndex]);
+ else
+ return STORE_PAGE_NULL;
+ }
+ void directLink (sal_uInt16 nIndex, sal_uInt32 nAddr)
+ {
+ if (nIndex < directCount)
+ m_aTable.m_pDirect[nIndex] = store::htonl(nAddr);
+ }
+
+ /** single.
+ */
+ static const sal_uInt16 singleCount = sal_uInt16(STORE_LIMIT_DATAPAGE_SINGLE);
+
+ sal_uInt32 singleLink (sal_uInt16 nIndex) const
+ {
+ if (nIndex < singleCount)
+ return store::ntohl(m_aTable.m_pSingle[nIndex]);
+ else
+ return STORE_PAGE_NULL;
+ }
+ void singleLink (sal_uInt16 nIndex, sal_uInt32 nAddr)
+ {
+ if (nIndex < singleCount)
+ m_aTable.m_pSingle[nIndex] = store::htonl(nAddr);
+ }
+
+ /** double.
+ */
+ static const sal_uInt16 doubleCount = sal_uInt16(STORE_LIMIT_DATAPAGE_DOUBLE);
+
+ sal_uInt32 doubleLink (sal_uInt16 nIndex) const
+ {
+ if (nIndex < doubleCount)
+ return store::ntohl(m_aTable.m_pDouble[nIndex]);
+ else
+ return STORE_PAGE_NULL;
+ }
+ void doubleLink (sal_uInt16 nIndex, sal_uInt32 nAddr)
+ {
+ if (nIndex < doubleCount)
+ m_aTable.m_pDouble[nIndex] = store::htonl(nAddr);
+ }
+
+ /** triple.
+ */
+ static const sal_uInt16 tripleCount = sal_uInt16(STORE_LIMIT_DATAPAGE_TRIPLE);
+
+ sal_uInt32 tripleLink (sal_uInt16 nIndex) const
+ {
+ if (nIndex < tripleCount)
+ return store::ntohl(m_aTable.m_pTriple[nIndex]);
+ else
+ return STORE_PAGE_NULL;
+ }
+ void tripleLink (sal_uInt16 nIndex, sal_uInt32 nAddr)
+ {
+ if (nIndex < tripleCount)
+ m_aTable.m_pTriple[nIndex] = store::htonl(nAddr);
+ }
+};
+
+#define STORE_MAGIC_DIRECTORYPAGE sal_uInt32(0x62190120)
+
+struct OStoreDirectoryPageData : public store::PageData
+{
+ typedef PageData base;
+ typedef OStoreDirectoryPageData self;
+
+ typedef OStorePageDescriptor D;
+ typedef OStorePageNameBlock NameBlock;
+ typedef OStoreDirectoryDataBlock DataBlock;
+
+ /** Representation.
+ */
+ NameBlock m_aNameBlock;
+ DataBlock m_aDataBlock;
+ sal_uInt8 m_pData[1];
+
+ /** type.
+ */
+ static const sal_uInt32 theTypeId = STORE_MAGIC_DIRECTORYPAGE;
+
+ /** size.
+ */
+ static const size_t theSize = NameBlock::theSize + DataBlock::theSize;
+ static const sal_uInt16 thePageSize = base::theSize + self::theSize;
+ static_assert(STORE_MINIMUM_PAGESIZE >= self::thePageSize, "got to be at least equal in size");
+
+ /** capacity.
+ */
+ sal_uInt16 capacity() const
+ {
+ return static_cast<sal_uInt16>(store::ntohs(base::m_aDescr.m_nSize) - self::thePageSize);
+ }
+
+ /** Construction.
+ */
+ explicit OStoreDirectoryPageData (sal_uInt16 nPageSize)
+ : base (nPageSize), m_aNameBlock(), m_aDataBlock()
+ {
+ base::m_aGuard.m_nMagic = store::htonl(self::theTypeId);
+ base::m_aDescr.m_nUsed = store::htons(self::thePageSize);
+ memset (m_pData, 0, capacity());
+ }
+
+ /** guard (external representation).
+ */
+ void guard()
+ {
+ m_aNameBlock.guard();
+ m_aDataBlock.guard();
+ }
+
+ /** verify (external representation).
+ */
+ storeError verify() const
+ {
+ storeError eErrCode = m_aNameBlock.verify();
+ if (eErrCode == store_E_None)
+ eErrCode = m_aDataBlock.verify();
+ return eErrCode;
+ }
+
+ /** ChunkDescriptor.
+ */
+ struct ChunkDescriptor
+ {
+ /** Representation.
+ */
+ sal_uInt32 m_nPage;
+ sal_uInt16 m_nOffset;
+ sal_uInt16 m_nLength;
+
+ /** Construction.
+ */
+ ChunkDescriptor (sal_uInt32 nPosition, sal_uInt16 nCapacity)
+ : m_nPage(nPosition / nCapacity),
+ m_nOffset(static_cast<sal_uInt16>((nPosition % nCapacity) & 0xffff)),
+ m_nLength(nCapacity - m_nOffset)
+ {
+ }
+ };
+
+ /** ChunkScope.
+ */
+ enum ChunkScope
+ {
+ SCOPE_INTERNAL,
+ SCOPE_EXTERNAL,
+ SCOPE_DIRECT,
+ SCOPE_SINGLE,
+ SCOPE_DOUBLE,
+ SCOPE_TRIPLE,
+ SCOPE_UNREACHABLE,
+ SCOPE_UNKNOWN
+ };
+
+ /** scope (internal).
+ */
+ ChunkScope scope (sal_uInt32 nPosition) const
+ {
+ sal_uInt32 nCapacity = capacity();
+ if (nPosition < nCapacity)
+ return SCOPE_INTERNAL;
+ else
+ return SCOPE_EXTERNAL;
+ }
+};
+
+class OStoreDirectoryPageObject : public store::OStorePageObject
+{
+ typedef OStorePageObject base;
+ typedef OStoreDirectoryPageData page;
+ typedef OStoreIndirectionPageData indirect;
+
+ typedef OStorePageDescriptor D;
+
+public:
+ /** Construction.
+ */
+ explicit OStoreDirectoryPageObject (std::shared_ptr<PageData> const & rxPage = std::shared_ptr<PageData>())
+ : OStorePageObject (rxPage)
+ {}
+
+ /** External representation.
+ */
+ virtual storeError guard (sal_uInt32 nAddr) override;
+ virtual storeError verify (sal_uInt32 nAddr) const override;
+
+ /** attrib.
+ */
+ sal_uInt32 attrib() const
+ {
+ return store::ntohl(PAGE().m_aNameBlock.m_nAttrib);
+ }
+ void attrib (sal_uInt32 nAttrib)
+ {
+ PAGE().m_aNameBlock.m_nAttrib = store::htonl(nAttrib);
+ touch();
+ }
+
+ /** key.
+ */
+ void key (OStorePageKey const & rKey)
+ {
+ PAGE().m_aNameBlock.m_aKey = rKey;
+ touch();
+ }
+
+ /** path.
+ */
+ sal_uInt32 path() const
+ {
+ page const & rPage = PAGE();
+ const char * pszName = rPage.m_aNameBlock.m_pData;
+ sal_uInt32 nPath = store::ntohl(rPage.m_aNameBlock.m_aKey.m_nHigh);
+ return rtl_crc32 (nPath, pszName, rtl_str_getLength(pszName));
+ }
+
+ /** dataLength.
+ */
+ sal_uInt32 dataLength() const
+ {
+ return store::ntohl(PAGE().m_aDataBlock.m_nDataLen);
+ }
+ void dataLength (sal_uInt32 nLength)
+ {
+ PAGE().m_aDataBlock.m_nDataLen = store::htonl(nLength);
+ touch();
+ }
+
+ /** direct.
+ */
+ sal_uInt32 directLink (sal_uInt16 nIndex) const
+ {
+ return PAGE().m_aDataBlock.directLink (nIndex);
+ }
+ void directLink (sal_uInt16 nIndex, sal_uInt32 nAddr)
+ {
+ PAGE().m_aDataBlock.directLink (nIndex, nAddr);
+ touch();
+ }
+
+ /** single indirect.
+ */
+ sal_uInt32 singleLink (sal_uInt16 nIndex) const
+ {
+ return PAGE().m_aDataBlock.singleLink (nIndex);
+ }
+ void singleLink (sal_uInt16 nIndex, sal_uInt32 nAddr)
+ {
+ PAGE().m_aDataBlock.singleLink (nIndex, nAddr);
+ touch();
+ }
+
+ /** double indirect.
+ */
+ sal_uInt32 doubleLink (sal_uInt16 nIndex) const
+ {
+ return PAGE().m_aDataBlock.doubleLink (nIndex);
+ }
+ void doubleLink (sal_uInt16 nIndex, sal_uInt32 nAddr)
+ {
+ PAGE().m_aDataBlock.doubleLink (nIndex, nAddr);
+ touch();
+ }
+
+ /** triple indirect.
+ */
+ sal_uInt32 tripleLink (sal_uInt16 nIndex) const
+ {
+ return PAGE().m_aDataBlock.tripleLink (nIndex);
+ }
+ void tripleLink (sal_uInt16 nIndex, sal_uInt32 nAddr)
+ {
+ PAGE().m_aDataBlock.tripleLink (nIndex, nAddr);
+ touch();
+ }
+
+ /** read (external data page).
+ */
+ storeError read (
+ sal_uInt32 nPage,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS) const;
+
+ /** write (external data page).
+ */
+ storeError write (
+ sal_uInt32 nPage,
+ OStoreDataPageObject &rData,
+ OStorePageBIOS &rBIOS);
+
+ /** truncate (external data page).
+ */
+ storeError truncate (
+ sal_uInt32 nPage,
+ OStorePageBIOS &rBIOS);
+
+private:
+ /** Representation.
+ */
+ page & PAGE()
+ {
+ page * pImpl = static_cast<page*>(m_xPage.get());
+ OSL_PRECOND(pImpl != nullptr, "OStoreDirectoryPageObject::PAGE(): Null pointer");
+ return (*pImpl);
+ }
+ page const & PAGE() const
+ {
+ page const * pImpl = static_cast<page const *>(m_xPage.get());
+ OSL_PRECOND(pImpl != nullptr, "OStoreDirectoryPageObject::PAGE(): Null pointer");
+ return (*pImpl);
+ }
+
+ /** scope (external data page; private).
+ */
+ page::ChunkScope scope (
+ sal_uInt32 nPage,
+ page::DataBlock::LinkDescriptor &rDescr) const;
+
+ /** truncate (external data page scope; private).
+ */
+ storeError truncate (
+ page::ChunkScope eScope,
+ sal_uInt16 nRemain,
+ OStorePageBIOS &rBIOS);
+};
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/stordir.cxx b/store/source/stordir.cxx
new file mode 100644
index 0000000000..26e60910c0
--- /dev/null
+++ b/store/source/stordir.cxx
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "stordir.hxx"
+
+#include <sal/types.h>
+
+#include <rtl/textcvt.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.h>
+
+#include <osl/mutex.hxx>
+
+#include <store/types.h>
+
+#include "storbase.hxx"
+#include "stordata.hxx"
+#include "storpage.hxx"
+
+using namespace store;
+
+static sal_Size convertTextToUnicode (
+ rtl_TextToUnicodeConverter hConverter,
+ const char *pSrcBuffer, sal_Size nSrcLength,
+ sal_Unicode *pDstBuffer, sal_Size nDstLength)
+{
+ sal_uInt32 nCvtInfo = 0;
+ sal_Size nCvtBytes = 0;
+ return rtl_convertTextToUnicode (
+ hConverter, nullptr,
+ pSrcBuffer, nSrcLength,
+ pDstBuffer, nDstLength,
+ OSTRING_TO_OUSTRING_CVTFLAGS,
+ &nCvtInfo, &nCvtBytes);
+}
+
+const sal_uInt32 OStoreDirectory_Impl::m_nTypeId(0x89191107);
+
+OStoreDirectory_Impl::OStoreDirectory_Impl()
+ : m_aDescr (0, 0, 0),
+ m_nPath (0),
+ m_hTextCvt (nullptr)
+{}
+
+OStoreDirectory_Impl::~OStoreDirectory_Impl()
+{
+ if (m_xManager.is())
+ {
+ if (m_aDescr.m_nAddr != STORE_PAGE_NULL)
+ m_xManager->releasePage (m_aDescr);
+ }
+ rtl_destroyTextToUnicodeConverter (m_hTextCvt);
+}
+
+bool OStoreDirectory_Impl::isKindOf (sal_uInt32 nTypeId)
+{
+ return (nTypeId == m_nTypeId);
+}
+
+storeError OStoreDirectory_Impl::create (
+ OStorePageManager *pManager,
+ rtl_String const *pPath,
+ rtl_String const *pName,
+ storeAccessMode eMode)
+{
+ rtl::Reference<OStorePageManager> xManager (pManager);
+ if (!xManager.is())
+ return store_E_InvalidAccess;
+
+ if (!(pPath && pName))
+ return store_E_InvalidParameter;
+
+ OStoreDirectoryPageObject aPage;
+ storeError eErrCode = xManager->iget (
+ aPage, STORE_ATTRIB_ISDIR,
+ pPath, pName, eMode);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ if (!(aPage.attrib() & STORE_ATTRIB_ISDIR))
+ return store_E_NotDirectory;
+
+ inode_holder_type xNode (aPage.get());
+ eErrCode = xManager->acquirePage (xNode->m_aDescr, storeAccessMode::ReadOnly);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Evaluate iteration path.
+ m_nPath = aPage.path();
+ m_nPath = rtl_crc32 (m_nPath, "/", 1);
+
+ // Save page manager, and descriptor.
+ m_xManager = xManager;
+ m_aDescr = xNode->m_aDescr;
+
+ return store_E_None;
+}
+
+storeError OStoreDirectory_Impl::iterate (storeFindData &rFindData)
+{
+ if (!m_xManager.is())
+ return store_E_InvalidAccess;
+
+ storeError eErrCode = store_E_NoMoreFiles;
+ if (!rFindData.m_nReserved)
+ return eErrCode;
+
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (*m_xManager);
+
+ // Check TextConverter.
+ if (m_hTextCvt == nullptr)
+ m_hTextCvt = rtl_createTextToUnicodeConverter(RTL_TEXTENCODING_UTF8);
+
+ // Setup iteration key.
+ OStorePageKey aKey (rFindData.m_nReserved, m_nPath);
+
+ // Iterate.
+ for (;;)
+ {
+ OStorePageLink aLink;
+ eErrCode = m_xManager->iterate (aKey, aLink, rFindData.m_nAttrib);
+ if (eErrCode != store_E_None || aKey.m_nHigh != store::htonl(m_nPath))
+ break;
+
+ if (!(rFindData.m_nAttrib & STORE_ATTRIB_ISLINK))
+ {
+ // Load page.
+ OStoreDirectoryPageObject aPage;
+ eErrCode = m_xManager->loadObjectAt (aPage, aLink.location());
+ if (eErrCode == store_E_None)
+ {
+ inode_holder_type xNode (aPage.get());
+
+ // Setup FindData.
+ char *p = xNode->m_aNameBlock.m_pData;
+ sal_Int32 n = rtl_str_getLength (p);
+ sal_Int32 k = rFindData.m_nLength;
+
+ n = convertTextToUnicode (
+ m_hTextCvt, p, n,
+ rFindData.m_pszName, STORE_MAXIMUM_NAMESIZE - 1);
+ if (k > n)
+ {
+ k = (k - n) * sizeof(sal_Unicode);
+ memset (&rFindData.m_pszName[n], 0, k);
+ }
+
+ rFindData.m_nLength = n;
+ rFindData.m_nAttrib |= aPage.attrib();
+
+ // Leave.
+ rFindData.m_nReserved = store::ntohl(aKey.m_nLow);
+ return store_E_None;
+ }
+ }
+
+ if (aKey.m_nLow == 0)
+ break;
+ aKey.m_nLow = store::htonl(store::ntohl(aKey.m_nLow) - 1);
+ }
+
+ // Finished.
+ memset (&rFindData, 0, sizeof (storeFindData));
+ return store_E_NoMoreFiles;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/stordir.hxx b/store/source/stordir.hxx
new file mode 100644
index 0000000000..39e2242937
--- /dev/null
+++ b/store/source/stordir.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+#include <rtl/textcvt.h>
+#include <rtl/string.h>
+#include <rtl/ref.hxx>
+
+#include "object.hxx"
+#include "storbase.hxx"
+
+namespace store
+{
+
+class OStorePageManager;
+struct OStoreDirectoryPageData;
+
+class OStoreDirectory_Impl : public store::OStoreObject
+{
+public:
+ OStoreDirectory_Impl();
+
+ /** create (two-phase construction).
+ * @param pManager [in]
+ * @param pPath [in]
+ * @param pName [in]
+ * @param eAccessMode [in]
+ * @return store_E_None upon success.
+ */
+ storeError create (
+ OStorePageManager *pManager,
+ rtl_String const *pPath,
+ rtl_String const *pName,
+ storeAccessMode eAccessMode);
+
+ /** iterate.
+ * @param rFindData [out]
+ * @return store_E_None upon success,
+ * store_E_NoMoreFiles upon end of iteration.
+ */
+ storeError iterate (
+ storeFindData &rFindData);
+
+ virtual bool isKindOf (sal_uInt32 nTypeId) override;
+
+protected:
+ virtual ~OStoreDirectory_Impl() override;
+
+private:
+ /** IStoreHandle TypeId.
+ */
+ static const sal_uInt32 m_nTypeId;
+
+ /** IStoreHandle query() template function specialization.
+ */
+ friend OStoreDirectory_Impl*
+ SAL_CALL query<> (OStoreObject *pHandle, OStoreDirectory_Impl*);
+
+ typedef OStoreDirectoryPageData inode;
+ typedef PageHolderObject< inode > inode_holder_type;
+
+ rtl::Reference<OStorePageManager> m_xManager;
+
+ OStorePageDescriptor m_aDescr;
+ sal_uInt32 m_nPath;
+ rtl_TextToUnicodeConverter m_hTextCvt;
+
+ OStoreDirectory_Impl (const OStoreDirectory_Impl&) = delete;
+ OStoreDirectory_Impl& operator= (const OStoreDirectory_Impl&) = delete;
+};
+
+template<> inline OStoreDirectory_Impl*
+SAL_CALL query (OStoreObject *pHandle, SAL_UNUSED_PARAMETER OStoreDirectory_Impl*)
+{
+ if (pHandle && pHandle->isKindOf (OStoreDirectory_Impl::m_nTypeId))
+ {
+ // Handle is kind of OStoreDirectory_Impl.
+ return static_cast<OStoreDirectory_Impl*>(pHandle);
+ }
+ return nullptr;
+}
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/store.cxx b/store/source/store.cxx
new file mode 100644
index 0000000000..254e064915
--- /dev/null
+++ b/store/source/store.cxx
@@ -0,0 +1,371 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <store/store.h>
+
+#include <sal/types.h>
+#include <rtl/string.hxx>
+#include <rtl/ref.hxx>
+
+#include "object.hxx"
+#include "lockbyte.hxx"
+
+#include "storbase.hxx"
+#include "storpage.hxx"
+#include "stordir.hxx"
+#include "storlckb.hxx"
+
+using rtl::Reference;
+
+namespace store
+{
+
+namespace {
+
+/** Template helper class as type safe Reference to store_handle_type.
+ */
+template<class store_handle_type>
+class OStoreHandle : public rtl::Reference<store_handle_type>
+{
+public:
+ explicit OStoreHandle (store_handle_type * pHandle)
+ : rtl::Reference<store_handle_type> (pHandle)
+ {}
+
+ static store_handle_type * SAL_CALL query (void * pHandle)
+ {
+ return store::query (
+ static_cast<OStoreObject*>(pHandle),
+ static_cast<store_handle_type*>(nullptr));
+ }
+};
+
+}
+
+}
+
+using namespace store;
+
+storeError store_acquireHandle (
+ storeHandle Handle
+) SAL_THROW_EXTERN_C()
+{
+ OStoreObject *pHandle = static_cast<OStoreObject*>(Handle);
+ if (!pHandle)
+ return store_E_InvalidHandle;
+
+ pHandle->acquire();
+ return store_E_None;
+}
+
+storeError store_releaseHandle (
+ storeHandle Handle
+) SAL_THROW_EXTERN_C()
+{
+ OStoreObject *pHandle = static_cast<OStoreObject*>(Handle);
+ if (!pHandle)
+ return store_E_InvalidHandle;
+
+ pHandle->release();
+ return store_E_None;
+}
+
+storeError store_createMemoryFile (
+ sal_uInt16 nPageSize,
+ storeFileHandle *phFile
+) SAL_THROW_EXTERN_C()
+{
+ if (!phFile)
+ return store_E_InvalidParameter;
+ *phFile = nullptr;
+
+ Reference<ILockBytes> xLockBytes;
+
+ storeError eErrCode = MemoryLockBytes_createInstance(xLockBytes);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ OSL_ASSERT(xLockBytes.is());
+
+ Reference<OStorePageManager> xManager (new OStorePageManager());
+ if (!xManager.is())
+ return store_E_OutOfMemory;
+
+ eErrCode = xManager->initialize (
+ &*xLockBytes, storeAccessMode::Create, nPageSize);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ xManager->acquire();
+
+ *phFile = xManager.get();
+ return store_E_None;
+}
+
+storeError store_openFile (
+ rtl_uString *pFilename,
+ storeAccessMode eAccessMode,
+ sal_uInt16 nPageSize,
+ storeFileHandle *phFile
+) SAL_THROW_EXTERN_C()
+{
+ if (phFile)
+ *phFile = nullptr;
+
+ if (!(pFilename && phFile))
+ return store_E_InvalidParameter;
+
+ Reference<ILockBytes> xLockBytes;
+
+ storeError eErrCode = FileLockBytes_createInstance (xLockBytes, pFilename, eAccessMode);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ OSL_ASSERT(xLockBytes.is());
+
+ Reference<OStorePageManager> xManager (new OStorePageManager());
+ if (!xManager.is())
+ return store_E_OutOfMemory;
+
+ eErrCode = xManager->initialize (
+ &*xLockBytes, eAccessMode, nPageSize);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ xManager->acquire();
+
+ *phFile = xManager.get();
+ return store_E_None;
+}
+
+/*
+ * store_closeFile.
+ */
+storeError store_closeFile (
+ storeFileHandle Handle
+) SAL_THROW_EXTERN_C()
+{
+ OStorePageManager *pManager =
+ OStoreHandle<OStorePageManager>::query (Handle);
+ if (!pManager)
+ return store_E_InvalidHandle;
+
+ storeError eErrCode = pManager->close();
+ pManager->release();
+ return eErrCode;
+}
+
+storeError store_flushFile (
+ storeFileHandle Handle
+) SAL_THROW_EXTERN_C()
+{
+ OStoreHandle<OStorePageManager> xManager (
+ OStoreHandle<OStorePageManager>::query (Handle));
+ if (!xManager.is())
+ return store_E_InvalidHandle;
+
+ return xManager->flush();
+}
+
+storeError store_openDirectory (
+ storeFileHandle hFile,
+ rtl_uString const *pPath,
+ rtl_uString const *pName,
+ storeAccessMode eAccessMode,
+ storeDirectoryHandle *phDirectory
+) SAL_THROW_EXTERN_C()
+{
+ storeError eErrCode = store_E_None;
+ if (phDirectory)
+ *phDirectory = nullptr;
+
+ OStoreHandle<OStorePageManager> xManager (
+ OStoreHandle<OStorePageManager>::query (hFile));
+ if (!xManager.is())
+ return store_E_InvalidHandle;
+
+ if (!(pPath && pName && phDirectory))
+ return store_E_InvalidParameter;
+
+ Reference<OStoreDirectory_Impl> xDirectory (new OStoreDirectory_Impl());
+ if (!xDirectory.is())
+ return store_E_OutOfMemory;
+
+ OString aPath (pPath->buffer, pPath->length, RTL_TEXTENCODING_UTF8);
+ OString aName (pName->buffer, pName->length, RTL_TEXTENCODING_UTF8);
+
+ eErrCode = xDirectory->create (&*xManager, aPath.pData, aName.pData, eAccessMode);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ xDirectory->acquire();
+
+ *phDirectory = xDirectory.get();
+ return store_E_None;
+}
+
+storeError store_findFirst (
+ storeDirectoryHandle Handle,
+ storeFindData *pFindData
+) SAL_THROW_EXTERN_C()
+{
+ OStoreHandle<OStoreDirectory_Impl> xDirectory (
+ OStoreHandle<OStoreDirectory_Impl>::query (Handle));
+ if (!xDirectory.is())
+ return store_E_InvalidHandle;
+
+ if (!pFindData)
+ return store_E_InvalidParameter;
+
+ // Initialize FindData.
+ memset (pFindData, 0, sizeof (storeFindData));
+
+ // Find first.
+ pFindData->m_nReserved = sal_uInt32(~0);
+ return xDirectory->iterate (*pFindData);
+}
+
+storeError store_findNext (
+ storeDirectoryHandle Handle,
+ storeFindData *pFindData
+) SAL_THROW_EXTERN_C()
+{
+ OStoreHandle<OStoreDirectory_Impl> xDirectory (
+ OStoreHandle<OStoreDirectory_Impl>::query (Handle));
+ if (!xDirectory.is())
+ return store_E_InvalidHandle;
+
+ if (!pFindData)
+ return store_E_InvalidParameter;
+
+ // Check FindData.
+ if (!pFindData->m_nReserved)
+ return store_E_NoMoreFiles;
+
+ // Find next.
+ pFindData->m_nReserved -= 1;
+ return xDirectory->iterate (*pFindData);
+}
+
+storeError store_openStream (
+ storeFileHandle hFile,
+ rtl_uString const *pPath,
+ rtl_uString const *pName,
+ storeAccessMode eAccessMode,
+ storeStreamHandle *phStream
+) SAL_THROW_EXTERN_C()
+{
+ storeError eErrCode = store_E_None;
+ if (phStream)
+ *phStream = nullptr;
+
+ OStoreHandle<OStorePageManager> xManager (
+ OStoreHandle<OStorePageManager>::query (hFile));
+ if (!xManager.is())
+ return store_E_InvalidHandle;
+
+ if (!(pPath && pName && phStream))
+ return store_E_InvalidParameter;
+
+ Reference<OStoreLockBytes> xLockBytes (new OStoreLockBytes());
+ if (!xLockBytes.is())
+ return store_E_OutOfMemory;
+
+ OString aPath (pPath->buffer, pPath->length, RTL_TEXTENCODING_UTF8);
+ OString aName (pName->buffer, pName->length, RTL_TEXTENCODING_UTF8);
+
+ eErrCode = xLockBytes->create (&*xManager, aPath.pData, aName.pData, eAccessMode);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ xLockBytes->acquire();
+
+ *phStream = xLockBytes.get();
+ return store_E_None;
+}
+
+/*
+ * store_readStream.
+ */
+storeError store_readStream (
+ storeStreamHandle Handle,
+ sal_uInt32 nOffset,
+ void *pBuffer,
+ sal_uInt32 nBytes,
+ sal_uInt32 *pnDone
+) SAL_THROW_EXTERN_C()
+{
+ OStoreHandle<OStoreLockBytes> xLockBytes (
+ OStoreHandle<OStoreLockBytes>::query (Handle));
+ if (!xLockBytes.is())
+ return store_E_InvalidHandle;
+
+ if (!(pBuffer && pnDone))
+ return store_E_InvalidParameter;
+
+ return xLockBytes->readAt (nOffset, pBuffer, nBytes, *pnDone);
+}
+
+storeError store_writeStream (
+ storeStreamHandle Handle,
+ sal_uInt32 nOffset,
+ const void *pBuffer,
+ sal_uInt32 nBytes,
+ sal_uInt32 *pnDone
+) SAL_THROW_EXTERN_C()
+{
+ OStoreHandle<OStoreLockBytes> xLockBytes (
+ OStoreHandle<OStoreLockBytes>::query (Handle));
+ if (!xLockBytes.is())
+ return store_E_InvalidHandle;
+
+ if (!(pBuffer && pnDone))
+ return store_E_InvalidParameter;
+
+ return xLockBytes->writeAt (nOffset, pBuffer, nBytes, *pnDone);
+}
+
+storeError store_remove (
+ storeFileHandle Handle,
+ rtl_uString const *pPath,
+ rtl_uString const *pName
+) SAL_THROW_EXTERN_C()
+{
+ storeError eErrCode = store_E_None;
+
+ OStoreHandle<OStorePageManager> xManager (
+ OStoreHandle<OStorePageManager>::query (Handle));
+ if (!xManager.is())
+ return store_E_InvalidHandle;
+
+ if (!(pPath && pName))
+ return store_E_InvalidParameter;
+
+ // Setup page key.
+ OString aPath (pPath->buffer, pPath->length, RTL_TEXTENCODING_UTF8);
+ OString aName (pName->buffer, pName->length, RTL_TEXTENCODING_UTF8);
+ OStorePageKey aKey;
+
+ eErrCode = OStorePageManager::namei (aPath.pData, aName.pData, aKey);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Remove.
+ return xManager->remove (aKey);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storlckb.cxx b/store/source/storlckb.cxx
new file mode 100644
index 0000000000..bde235ad8e
--- /dev/null
+++ b/store/source/storlckb.cxx
@@ -0,0 +1,387 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "storlckb.hxx"
+
+#include <sal/types.h>
+#include <rtl/string.h>
+#include <rtl/ref.hxx>
+#include <osl/mutex.hxx>
+
+#include <store/types.h>
+
+#include "storbase.hxx"
+#include "stordata.hxx"
+#include "storpage.hxx"
+
+using namespace store;
+
+const sal_uInt32 OStoreLockBytes::m_nTypeId(0x94190310);
+
+OStoreLockBytes::OStoreLockBytes()
+ : m_bWriteable (false)
+{
+}
+
+OStoreLockBytes::~OStoreLockBytes()
+{
+ if (m_xManager.is() && m_xNode.is())
+ {
+ m_xManager->releasePage(m_xNode->m_aDescr);
+ }
+}
+
+bool OStoreLockBytes::isKindOf (sal_uInt32 nTypeId)
+{
+ return (nTypeId == m_nTypeId);
+}
+
+storeError OStoreLockBytes::create (
+ OStorePageManager *pManager,
+ rtl_String const *pPath,
+ rtl_String const *pName,
+ storeAccessMode eMode)
+{
+ rtl::Reference<OStorePageManager> xManager (pManager);
+ if (!xManager.is())
+ return store_E_InvalidAccess;
+
+ if (!(pPath && pName))
+ return store_E_InvalidParameter;
+
+ OStoreDirectoryPageObject aPage;
+ storeError eErrCode = xManager->iget (
+ aPage, STORE_ATTRIB_ISFILE,
+ pPath, pName, eMode);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ if (!(aPage.attrib() & STORE_ATTRIB_ISFILE))
+ {
+ // No ISFILE in older versions (backward compatibility).
+ if (aPage.attrib() & STORE_ATTRIB_ISLINK)
+ return store_E_NotFile;
+ }
+
+ inode_holder_type xNode (aPage.get());
+ if (eMode != storeAccessMode::ReadOnly)
+ eErrCode = xManager->acquirePage (xNode->m_aDescr, storeAccessMode::ReadWrite);
+ else
+ eErrCode = xManager->acquirePage (xNode->m_aDescr, storeAccessMode::ReadOnly);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ m_xManager = xManager;
+ m_xNode = xNode;
+ m_bWriteable = (eMode != storeAccessMode::ReadOnly);
+
+ // Check for truncation.
+ if (eMode == storeAccessMode::Create)
+ {
+ // Truncate to zero length.
+ eErrCode = setSize(0);
+ }
+ return eErrCode;
+}
+
+storeError OStoreLockBytes::readAt (
+ sal_uInt32 nOffset,
+ void *pBuffer,
+ sal_uInt32 nBytes,
+ sal_uInt32 &rnDone)
+{
+ rnDone = 0;
+
+ if (!m_xManager.is())
+ return store_E_InvalidAccess;
+
+ if (!pBuffer)
+ return store_E_InvalidParameter;
+ if (!nBytes)
+ return store_E_None;
+
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (*m_xManager);
+
+ // Determine data length.
+ OStoreDirectoryPageObject aPage (m_xNode.get());
+
+ sal_uInt32 nDataLen = aPage.dataLength();
+ if ((nOffset + nBytes) > nDataLen)
+ nBytes = nDataLen - nOffset;
+
+ // Read data.
+ OStoreDataPageObject aData;
+ sal_uInt8 *pData = static_cast<sal_uInt8*>(pBuffer);
+ while ((0 < nBytes) && (nOffset < nDataLen))
+ {
+ // Determine 'Offset' scope.
+ inode::ChunkScope eScope = m_xNode->scope (nOffset);
+ if (eScope == inode::SCOPE_INTERNAL)
+ {
+ // Read from inode page (internal scope).
+ inode::ChunkDescriptor aDescr (
+ nOffset, m_xNode->capacity());
+
+ sal_uInt32 nLength = sal_uInt32(aDescr.m_nLength);
+ if(nLength > nBytes)
+ {
+ nLength = nBytes;
+ }
+ memcpy (
+ &pData[rnDone],
+ &m_xNode->m_pData[aDescr.m_nOffset],
+ nLength);
+
+ // Adjust counters.
+ rnDone += nLength;
+ nOffset += nLength;
+ nBytes -= nLength;
+ }
+ else
+ {
+ // Read from data page (external scope).
+ inode::ChunkDescriptor aDescr (
+ nOffset - m_xNode->capacity(), OStoreDataPageData::capacity(m_xNode->m_aDescr)); // @@@
+
+ sal_uInt32 nLength = sal_uInt32(aDescr.m_nLength);
+ if(nLength > nBytes)
+ {
+ nLength = nBytes;
+ }
+
+ storeError eErrCode = aPage.read (aDescr.m_nPage, aData, *m_xManager);
+ if (eErrCode != store_E_None)
+ {
+ if (eErrCode != store_E_NotExists)
+ return eErrCode;
+
+ memset (
+ &pData[rnDone],
+ 0,
+ nLength);
+ }
+ else
+ {
+ PageHolderObject< data > xData (aData.makeHolder<data>());
+ memcpy (
+ &pData[rnDone],
+ &xData->m_pData[aDescr.m_nOffset],
+ nLength);
+ }
+
+ // Adjust counters.
+ rnDone += nLength;
+ nOffset += nLength;
+ nBytes -= nLength;
+ }
+ }
+
+ // Done.
+ return store_E_None;
+}
+
+storeError OStoreLockBytes::writeAt (
+ sal_uInt32 nOffset,
+ const void *pBuffer,
+ sal_uInt32 nBytes,
+ sal_uInt32 &rnDone)
+{
+ rnDone = 0;
+
+ if (!m_xManager.is())
+ return store_E_InvalidAccess;
+ if (!m_bWriteable)
+ return store_E_AccessViolation;
+
+ if (!pBuffer)
+ return store_E_InvalidParameter;
+ if (!nBytes)
+ return store_E_None;
+
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (*m_xManager);
+
+ // Write data.
+ OStoreDirectoryPageObject aPage (m_xNode.get());
+ const sal_uInt8 *pData = static_cast<const sal_uInt8*>(pBuffer);
+
+ storeError eErrCode = store_E_None;
+ while (nBytes > 0)
+ {
+ // Determine 'Offset' scope.
+ inode::ChunkScope eScope = m_xNode->scope (nOffset);
+ if (eScope == inode::SCOPE_INTERNAL)
+ {
+ // Write to inode page (internal scope).
+ inode::ChunkDescriptor aDescr (
+ nOffset, m_xNode->capacity());
+
+ sal_uInt32 nLength = sal_uInt32(aDescr.m_nLength);
+ if(nLength > nBytes)
+ {
+ nLength = nBytes;
+ }
+
+ memcpy (
+ &m_xNode->m_pData[aDescr.m_nOffset],
+ &pData[rnDone], nLength);
+
+ // Mark inode dirty.
+ aPage.touch();
+
+ // Adjust counters.
+ rnDone += nLength;
+ nOffset += nLength;
+ nBytes -= nLength;
+
+ // Adjust data length.
+ if (aPage.dataLength() < nOffset)
+ aPage.dataLength (nOffset);
+ }
+ else
+ {
+ // Write to data page (external scope).
+ OStoreDataPageObject aData;
+
+ inode::ChunkDescriptor aDescr (
+ nOffset - m_xNode->capacity(), OStoreDataPageData::capacity(m_xNode->m_aDescr)); // @@@
+
+ sal_uInt32 nLength = sal_uInt32(aDescr.m_nLength);
+ if ((aDescr.m_nOffset > 0) || (nBytes < nLength))
+ {
+ // Unaligned. Need to load/create data page.
+ eErrCode = aPage.read (aDescr.m_nPage, aData, *m_xManager);
+ if (eErrCode != store_E_None)
+ {
+ if (eErrCode != store_E_NotExists)
+ return eErrCode;
+
+ eErrCode = aData.construct<data>(m_xManager->allocator());
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ }
+ }
+
+ PageHolderObject< data > xData (aData.makeHolder<data>());
+ if (!xData.is())
+ {
+ eErrCode = aData.construct<data>(m_xManager->allocator());
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ xData = aData.makeHolder<data>();
+ }
+
+ // Modify data page.
+ if(nLength > nBytes)
+ {
+ nLength = nBytes;
+ }
+ memcpy (
+ &xData->m_pData[aDescr.m_nOffset],
+ &pData[rnDone], nLength);
+
+ // Save data page.
+ eErrCode = aPage.write (aDescr.m_nPage, aData, *m_xManager);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Adjust counters.
+ rnDone += nLength;
+ nOffset += nLength;
+ nBytes -= nLength;
+
+ // Adjust data length.
+ if (aPage.dataLength() < nOffset)
+ aPage.dataLength (nOffset);
+ }
+ }
+
+ // Check for modified inode.
+ if (aPage.dirty())
+ return m_xManager->saveObjectAt (aPage, aPage.location());
+ else
+ return store_E_None;
+}
+
+storeError OStoreLockBytes::setSize (sal_uInt32 nSize)
+{
+ if (!m_xManager.is())
+ return store_E_InvalidAccess;
+ if (!m_bWriteable)
+ return store_E_AccessViolation;
+
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (*m_xManager);
+
+ // Determine current length.
+ OStoreDirectoryPageObject aPage (m_xNode.get());
+ sal_uInt32 nDataLen = aPage.dataLength();
+
+ if (nSize == nDataLen)
+ return store_E_None;
+
+ if (nSize < nDataLen)
+ {
+ // Truncate.
+ storeError eErrCode = store_E_None;
+
+ // Determine 'Size' scope.
+ inode::ChunkScope eSizeScope = m_xNode->scope (nSize);
+ if (eSizeScope == inode::SCOPE_INTERNAL)
+ {
+ // Internal 'Size' scope. Determine 'Data' scope.
+ inode::ChunkScope eDataScope = m_xNode->scope (nDataLen);
+ if (eDataScope == inode::SCOPE_EXTERNAL)
+ {
+ // External 'Data' scope. Truncate all external data pages.
+ eErrCode = aPage.truncate (0, *m_xManager);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ }
+
+ // Truncate internal data page.
+ inode::ChunkDescriptor aDescr (nSize, m_xNode->capacity());
+ memset (
+ &(m_xNode->m_pData[aDescr.m_nOffset]),
+ 0, aDescr.m_nLength);
+ }
+ else
+ {
+ // External 'Size' scope. Truncate external data pages.
+ inode::ChunkDescriptor aDescr (
+ nSize - m_xNode->capacity(), OStoreDataPageData::capacity(m_xNode->m_aDescr)); // @@@
+
+ sal_uInt32 nPage = aDescr.m_nPage;
+ if (aDescr.m_nOffset) nPage += 1;
+
+ eErrCode = aPage.truncate (nPage, *m_xManager);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ }
+ }
+
+ // Set (extended or truncated) size.
+ aPage.dataLength (nSize);
+
+ // Save modified inode.
+ return m_xManager->saveObjectAt (aPage, aPage.location());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storlckb.hxx b/store/source/storlckb.hxx
new file mode 100644
index 0000000000..d6712a897e
--- /dev/null
+++ b/store/source/storlckb.hxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+#include <rtl/string.h>
+#include <rtl/ref.hxx>
+
+#include "object.hxx"
+#include "storbase.hxx"
+
+namespace store
+{
+
+class OStorePageManager;
+struct OStoreDataPageData;
+struct OStoreDirectoryPageData;
+
+class OStoreLockBytes : public store::OStoreObject
+{
+public:
+ OStoreLockBytes();
+
+ /** create (two-phase construction).
+ * @param pManager [in]
+ * @param pPath [in]
+ * @param pName [in]
+ * @param eMode [in]
+ * @return store_E_None upon success
+ */
+ storeError create (
+ OStorePageManager *pManager,
+ rtl_String const *pPath,
+ rtl_String const *pName,
+ storeAccessMode eAccessMode);
+
+ /** Read at Offset into Buffer.
+ * @param nOffset [in]
+ * @param pBuffer [out]
+ * @param nBytes [in]
+ * @param rnDone [out]
+ * @return store_E_None upon success
+ */
+ storeError readAt (
+ sal_uInt32 nOffset,
+ void *pBuffer,
+ sal_uInt32 nBytes,
+ sal_uInt32 &rnDone);
+
+ /** Write at Offset from Buffer.
+ * @param nOffset [in]
+ * @param pBuffer [in]
+ * @param nBytes [in]
+ * @param rnDone [out]
+ * @return store_E_None upon success
+ */
+ storeError writeAt (
+ sal_uInt32 nOffset,
+ const void *pBuffer,
+ sal_uInt32 nBytes,
+ sal_uInt32 &rnDone);
+
+ /** setSize.
+ * @param nSize [in]
+ * @return store_E_None upon success
+ */
+ storeError setSize (sal_uInt32 nSize);
+
+ /** IStoreHandle.
+ */
+ virtual bool isKindOf (sal_uInt32 nMagic) override;
+
+protected:
+ virtual ~OStoreLockBytes() override;
+
+private:
+ /** IStoreHandle TypeId.
+ */
+ static const sal_uInt32 m_nTypeId;
+
+ /** IStoreHandle query() template specialization.
+ */
+ friend OStoreLockBytes*
+ SAL_CALL query<> (OStoreObject *pHandle, OStoreLockBytes*);
+
+ rtl::Reference<OStorePageManager> m_xManager;
+
+ typedef OStoreDataPageData data;
+ typedef OStoreDirectoryPageData inode;
+
+ typedef PageHolderObject< inode > inode_holder_type;
+ inode_holder_type m_xNode;
+
+ bool m_bWriteable;
+
+ OStoreLockBytes (const OStoreLockBytes&) = delete;
+ OStoreLockBytes& operator= (const OStoreLockBytes&) = delete;
+};
+
+template<> inline OStoreLockBytes*
+SAL_CALL query (OStoreObject *pHandle, SAL_UNUSED_PARAMETER OStoreLockBytes*)
+{
+ if (pHandle && pHandle->isKindOf (OStoreLockBytes::m_nTypeId))
+ {
+ // Handle is kind of OStoreLockBytes.
+ return static_cast<OStoreLockBytes*>(pHandle);
+ }
+ return nullptr;
+}
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storpage.cxx b/store/source/storpage.cxx
new file mode 100644
index 0000000000..df7e8cc175
--- /dev/null
+++ b/store/source/storpage.cxx
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "storpage.hxx"
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <rtl/string.h>
+#include <osl/mutex.hxx>
+
+#include <store/types.h>
+
+#include "storbase.hxx"
+#include "stordata.hxx"
+#include "stortree.hxx"
+
+using namespace store;
+
+const sal_uInt32 OStorePageManager::m_nTypeId = sal_uInt32(0x62190120);
+
+OStorePageManager::OStorePageManager()
+{
+}
+
+OStorePageManager::~OStorePageManager()
+{
+}
+
+bool OStorePageManager::isKindOf (sal_uInt32 nTypeId)
+{
+ return (nTypeId == m_nTypeId);
+}
+
+/*
+ * initialize (two-phase construction).
+ * Precond: none.
+ */
+storeError OStorePageManager::initialize (
+ ILockBytes * pLockBytes,
+ storeAccessMode eAccessMode,
+ sal_uInt16 & rnPageSize)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard(*this);
+
+ // Check arguments.
+ if (!pLockBytes)
+ return store_E_InvalidParameter;
+
+ // Initialize base.
+ storeError eErrCode = base::initialize (pLockBytes, eAccessMode, rnPageSize);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Check for (not) writeable.
+ if (!base::isWriteable())
+ {
+ // Readonly. Load RootNode.
+ return base::loadObjectAt (m_aRoot, rnPageSize);
+ }
+
+ // Writeable. Load or Create RootNode.
+ eErrCode = m_aRoot.loadOrCreate (rnPageSize, *this);
+ if (eErrCode == store_E_Pending)
+ {
+ // Creation notification.
+ PageHolderObject< page > xRoot (m_aRoot.get());
+
+ // Pre-allocate left most entry (ugly, but we can't insert to left).
+ OStorePageKey aKey (rtl_crc32 (0, "/", 1), 0);
+ xRoot->insert (0, entry(aKey));
+
+ // Save RootNode.
+ eErrCode = base::saveObjectAt (m_aRoot, rnPageSize);
+ }
+
+ // Done.
+ return eErrCode;
+}
+
+/*
+ * find_lookup (w/o split()).
+ * Internal: Precond: initialized, readable, exclusive access.
+ */
+storeError OStorePageManager::find_lookup (
+ OStoreBTreeNodeObject & rNode,
+ sal_uInt16 & rIndex,
+ OStorePageKey const & rKey)
+{
+ // Find Node and Index.
+ storeError eErrCode = m_aRoot.find_lookup (rNode, rIndex, rKey, *this);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Greater or Equal.
+ PageHolderObject< page > xPage (rNode.get());
+ SAL_WARN_IF(rIndex >= xPage->usageCount(), "store", "store::PageManager::find_lookup(): logic error");
+ entry e (xPage->m_pData[rIndex]);
+
+ // Check for exact match.
+ if (e.compare(entry(rKey)) != entry::COMPARE_EQUAL)
+ {
+ // Page not present.
+ return store_E_NotExists;
+ }
+
+ // Check address.
+ if (e.m_aLink.location() == STORE_PAGE_NULL)
+ {
+ // Page not present.
+ return store_E_NotExists;
+ }
+
+ return store_E_None;
+}
+
+/*
+ * remove_Impl (possibly down from root).
+ * Internal: Precond: initialized, writeable, exclusive access.
+ */
+
+storeError OStorePageManager::remove_Impl (entry & rEntry)
+{
+ OStoreBTreeNodeObject aNode (m_aRoot.get());
+
+ // Check current page index.
+ PageHolderObject< page > xPage (aNode.get());
+ sal_uInt16 i = xPage->find (rEntry), n = xPage->usageCount();
+ if (i >= n)
+ {
+ // Path to entry not exists (Must not happen(?)).
+ return store_E_NotExists;
+ }
+
+ // Compare entry.
+ entry::CompareResult result = rEntry.compare (xPage->m_pData[i]);
+
+ // Iterate down until equal match.
+ while ((result == entry::COMPARE_GREATER) && (xPage->depth() > 0))
+ {
+ // Check link address.
+ sal_uInt32 const nAddr = xPage->m_pData[i].m_aLink.location();
+ if (nAddr == STORE_PAGE_NULL)
+ {
+ // Path to entry not exists (Must not happen(?)).
+ return store_E_NotExists;
+ }
+
+ // Load link page.
+ storeError eErrCode = loadObjectAt (aNode, nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ PageHolderObject< page > xNext (aNode.get());
+ xNext.swap (xPage);
+
+ // Check index.
+ i = xPage->find (rEntry);
+ n = xPage->usageCount();
+ if (i >= n)
+ {
+ // Path to entry not exists (Must not happen(?)).
+ return store_E_NotExists;
+ }
+
+ // Compare entry.
+ result = rEntry.compare (xPage->m_pData[i]);
+ }
+
+ SAL_WARN_IF(
+ result == entry::COMPARE_LESS,
+ "store",
+ "OStorePageManager::remove(): find failed");
+
+ // Check entry comparison.
+ if (result == entry::COMPARE_LESS)
+ {
+ // Must not happen.
+ return store_E_Unknown;
+ }
+
+ // Remove down from current page (recursive).
+ return aNode.remove (i, rEntry, *this);
+}
+
+/*
+ * namei.
+ * Precond: none (static).
+ */
+storeError OStorePageManager::namei (
+ const rtl_String *pPath, const rtl_String *pName, OStorePageKey &rKey)
+{
+ // Check parameter.
+ if (!(pPath && pName))
+ return store_E_InvalidParameter;
+
+ // Check name length.
+ if (pName->length >= STORE_MAXIMUM_NAMESIZE)
+ return store_E_NameTooLong;
+
+ // Transform pathname into key.
+ rKey.m_nLow = store::htonl(rtl_crc32 (0, pName->buffer, pName->length));
+ rKey.m_nHigh = store::htonl(rtl_crc32 (0, pPath->buffer, pPath->length));
+
+ // Done.
+ return store_E_None;
+}
+
+/*
+ * iget.
+ * Precond: initialized.
+ */
+storeError OStorePageManager::iget (
+ OStoreDirectoryPageObject & rPage,
+ sal_uInt32 nAttrib,
+ const rtl_String * pPath,
+ const rtl_String * pName,
+ storeAccessMode eMode)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard(*this);
+
+ // Check precond.
+ if (!self::isValid())
+ return store_E_InvalidAccess;
+
+ // Setup inode page key.
+ OStorePageKey aKey;
+ storeError eErrCode = namei (pPath, pName, aKey);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Check for directory.
+ if (nAttrib & STORE_ATTRIB_ISDIR)
+ {
+ // Ugly, but necessary (backward compatibility).
+ aKey.m_nLow = store::htonl(rtl_crc32 (store::ntohl(aKey.m_nLow), "/", 1));
+ }
+
+ // Load inode page.
+ eErrCode = load_dirpage_Impl (aKey, rPage);
+ if (eErrCode != store_E_None)
+ {
+ // Check mode and reason.
+ if (eErrCode != store_E_NotExists)
+ return eErrCode;
+
+ if (eMode == storeAccessMode::ReadWrite)
+ return store_E_NotExists;
+ if (eMode == storeAccessMode::ReadOnly)
+ return store_E_NotExists;
+
+ if (!base::isWriteable())
+ return store_E_AccessViolation;
+
+ // Create inode page.
+ eErrCode = rPage.construct< inode >(base::allocator());
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Setup inode nameblock.
+ PageHolderObject< inode > xPage (rPage.get());
+
+ rPage.key (aKey);
+ rPage.attrib (nAttrib);
+
+ memcpy (
+ &(xPage->m_aNameBlock.m_pData[0]),
+ pName->buffer, pName->length);
+
+ // Save inode page.
+ eErrCode = save_dirpage_Impl (aKey, rPage);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ }
+
+ // Check for symbolic link.
+ if (rPage.attrib() & STORE_ATTRIB_ISLINK)
+ {
+ // Obtain 'Destination' page key.
+ PageHolderObject< inode > xPage (rPage.get());
+ OStorePageKey aDstKey;
+ memcpy (&aDstKey, &(xPage->m_pData[0]), sizeof(aDstKey));
+
+ // Load 'Destination' inode.
+ eErrCode = load_dirpage_Impl (aDstKey, rPage);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ }
+
+ // Done.
+ return store_E_None;
+}
+
+/*
+ * iterate.
+ * Precond: initialized.
+ * ToDo: skip hardlink entries.
+ */
+storeError OStorePageManager::iterate (
+ OStorePageKey & rKey,
+ OStorePageLink & rLink,
+ sal_uInt32 & rAttrib)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard(*this);
+
+ // Check precond.
+ if (!self::isValid())
+ return store_E_InvalidAccess;
+
+ // Find NodePage and Index.
+ OStoreBTreeNodeObject aNode;
+ sal_uInt16 i = 0;
+ storeError eErrCode = m_aRoot.find_lookup (aNode, i, rKey, *this);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // GreaterEqual. Found next entry.
+ PageHolderObject< page > xNode (aNode.get());
+ entry e (xNode->m_pData[i]);
+
+ // Setup result.
+ rKey = e.m_aKey;
+ rLink = e.m_aLink;
+ rAttrib = store::ntohl(e.m_nAttrib);
+
+ // Done.
+ return store_E_None;
+}
+
+/*
+ * load => private: iget() @@@
+ * Internal: Precond: initialized, exclusive access.
+ */
+storeError OStorePageManager::load_dirpage_Impl (
+ const OStorePageKey &rKey,
+ OStoreDirectoryPageObject &rPage)
+{
+ // Find Node and Index.
+ OStoreBTreeNodeObject aNode;
+ sal_uInt16 i = 0;
+ storeError eErrCode = find_lookup (aNode, i, rKey);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Existing entry. Load page.
+ PageHolderObject< page > xNode (aNode.get());
+ entry e (xNode->m_pData[i]);
+ return loadObjectAt (rPage, e.m_aLink.location());
+}
+
+/*
+ * save => private: iget(), rebuild() @@@
+ * Internal: Precond: initialized, writeable, exclusive access.
+ */
+storeError OStorePageManager::save_dirpage_Impl (
+ const OStorePageKey &rKey,
+ OStoreDirectoryPageObject &rPage)
+{
+ // Find NodePage and Index.
+ node aNode;
+ sal_uInt16 i = 0;
+
+ storeError eErrCode = m_aRoot.find_insert (aNode, i, rKey, *this);
+ PageHolderObject< page > xNode (aNode.get());
+ if (eErrCode != store_E_None)
+ {
+ if (eErrCode != store_E_AlreadyExists)
+ return eErrCode;
+
+ // Existing entry.
+ entry e (xNode->m_pData[i]);
+ if (e.m_aLink.location() != STORE_PAGE_NULL)
+ {
+ // Save page to existing location.
+ return saveObjectAt (rPage, e.m_aLink.location());
+ }
+
+ // Allocate page.
+ eErrCode = base::allocate (rPage);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Update page location.
+ xNode->m_pData[i].m_aLink = rPage.location();
+
+ // Save modified NodePage.
+ return saveObjectAt (aNode, aNode.location());
+ }
+
+ // Allocate page.
+ eErrCode = base::allocate (rPage);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Insert.
+ OStorePageLink aLink (rPage.location());
+ xNode->insert (i + 1, entry (rKey, aLink));
+
+ // Save modified NodePage.
+ return saveObjectAt (aNode, aNode.location());
+}
+
+/*
+ * remove.
+ * Precond: initialized, writeable.
+ */
+storeError OStorePageManager::remove (const OStorePageKey &rKey)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard(*this);
+
+ // Check precond.
+ if (!self::isValid())
+ return store_E_InvalidAccess;
+
+ if (!base::isWriteable())
+ return store_E_AccessViolation;
+
+ // Find NodePage and index.
+ OStoreBTreeNodeObject aNodePage;
+ sal_uInt16 i = 0;
+ storeError eErrCode = find_lookup (aNodePage, i, rKey);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Existing entry.
+ PageHolderObject< page > xNodePage (aNodePage.get());
+ entry e (xNodePage->m_pData[i]);
+
+ // Check for (not a) hardlink.
+ if (!(store::ntohl(e.m_nAttrib) & STORE_ATTRIB_ISLINK))
+ {
+ // Load directory page.
+ OStoreDirectoryPageObject aPage;
+ eErrCode = base::loadObjectAt (aPage, e.m_aLink.location());
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ inode_holder_type xNode (aPage.get());
+
+ // Acquire page write access.
+ OStorePageDescriptor aDescr (xNode->m_aDescr);
+ eErrCode = base::acquirePage (aDescr, storeAccessMode::ReadWrite);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Check for symbolic link.
+ if (!(aPage.attrib() & STORE_ATTRIB_ISLINK))
+ {
+ // Ordinary inode. Determine 'Data' scope.
+ inode::ChunkScope eScope = xNode->scope (aPage.dataLength());
+ if (eScope == inode::SCOPE_EXTERNAL)
+ {
+ // External 'Data' scope. Truncate all external data pages.
+ eErrCode = aPage.truncate (0, *this);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ }
+
+ // Truncate internal data page.
+ memset (&(xNode->m_pData[0]), 0, xNode->capacity());
+ aPage.dataLength (0);
+ }
+
+ // Release page write access.
+ base::releasePage (aDescr);
+
+ // Release and free directory page.
+ (void)base::free (aPage.location());
+ }
+
+ // Remove entry.
+ return remove_Impl (e);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/storpage.hxx b/store/source/storpage.hxx
new file mode 100644
index 0000000000..990808c7e6
--- /dev/null
+++ b/store/source/storpage.hxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/string.h>
+
+#include "object.hxx"
+
+#include "storbase.hxx"
+#include "storbios.hxx"
+#include "stortree.hxx"
+
+namespace store
+{
+
+class ILockBytes;
+struct OStoreDirectoryPageData;
+class OStoreDirectoryPageObject;
+
+class OStorePageManager : public store::OStorePageBIOS
+{
+public:
+ OStorePageManager();
+
+ /** Initialization (two-phase construction).
+ */
+ virtual storeError initialize (
+ ILockBytes * pLockBytes,
+ storeAccessMode eAccessMode,
+ sal_uInt16 & rnPageSize) override;
+
+ /** isValid.
+ * @return sal_True upon successful initialization,
+ * sal_False otherwise.
+ */
+ inline bool isValid() const;
+
+ /**
+ DirectoryPage I/O (managed).
+ */
+ static storeError namei (
+ const rtl_String *pPath,
+ const rtl_String *pName,
+ OStorePageKey &rKey);
+
+ storeError iget (
+ OStoreDirectoryPageObject & rPage, // [out]
+ sal_uInt32 nAttrib,
+ const rtl_String * pPath,
+ const rtl_String * pName,
+ storeAccessMode eMode);
+
+ storeError iterate (
+ OStorePageKey & rKey,
+ OStorePageLink & rLink,
+ sal_uInt32 & rAttrib);
+
+ /** remove.
+ * @see store_remove()
+ */
+ storeError remove (
+ const OStorePageKey &rKey);
+
+ /** IStoreHandle.
+ */
+ virtual bool isKindOf (sal_uInt32 nTypeId) override;
+
+protected:
+ virtual ~OStorePageManager() override;
+
+private:
+ typedef OStorePageBIOS base;
+ typedef OStorePageManager self;
+
+ typedef OStoreBTreeEntry entry;
+ typedef OStoreBTreeNodeData page;
+ typedef OStoreBTreeNodeObject node;
+
+ typedef OStoreDirectoryPageData inode;
+ typedef PageHolderObject< inode > inode_holder_type;
+
+ /** IStoreHandle TypeId.
+ */
+ static const sal_uInt32 m_nTypeId;
+
+ /** IStoreHandle query() template function specialization.
+ */
+ friend OStorePageManager*
+ SAL_CALL query<> (OStoreObject *pHandle, OStorePageManager*);
+
+ /** Representation.
+ */
+ OStoreBTreeRootObject m_aRoot;
+
+ /** DirectoryPage I/O (managed).
+ */
+ storeError load_dirpage_Impl(
+ const OStorePageKey &rKey,
+ OStoreDirectoryPageObject &rPage);
+
+ storeError save_dirpage_Impl(
+ const OStorePageKey &rKey,
+ OStoreDirectoryPageObject &rPage);
+
+ /** find_lookup (node page and index, w/o split).
+ */
+ storeError find_lookup (
+ OStoreBTreeNodeObject & rNode,
+ sal_uInt16 & rIndex,
+ OStorePageKey const & rKey);
+
+ /** remove (possibly down from root).
+ */
+ storeError remove_Impl (entry & rEntry);
+
+ OStorePageManager (const OStorePageManager&) = delete;
+ OStorePageManager& operator= (const OStorePageManager&) = delete;
+};
+
+inline bool OStorePageManager::isValid() const
+{
+ return base::isValid();
+}
+
+template<> inline OStorePageManager*
+SAL_CALL query (OStoreObject *pHandle, SAL_UNUSED_PARAMETER OStorePageManager*)
+{
+ if (pHandle && pHandle->isKindOf (OStorePageManager::m_nTypeId))
+ {
+ // Handle is kind of OStorePageManager.
+ return static_cast<OStorePageManager*>(pHandle);
+ }
+ return nullptr;
+}
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/stortree.cxx b/store/source/stortree.cxx
new file mode 100644
index 0000000000..e098508336
--- /dev/null
+++ b/store/source/stortree.cxx
@@ -0,0 +1,507 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+#include <string.h>
+
+#include "stortree.hxx"
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <store/types.h>
+
+#include "storbase.hxx"
+#include "storbios.hxx"
+
+using namespace store;
+
+OStoreBTreeNodeData::OStoreBTreeNodeData (sal_uInt16 nPageSize)
+ : PageData (nPageSize)
+{
+ base::m_aGuard.m_nMagic = store::htonl(self::theTypeId);
+ base::m_aDescr.m_nUsed = store::htons(self::thePageSize); // usageCount(0)
+ self::m_aGuard.m_nMagic = store::htonl(0); // depth(0)
+
+ sal_uInt16 const n = capacityCount();
+ T const t;
+
+ for (sal_uInt16 i = 1; i < n; i++)
+ {
+ // cppcheck-suppress arrayIndexOutOfBounds
+ m_pData[i] = t;
+ }
+}
+
+/*
+ * find.
+ */
+sal_uInt16 OStoreBTreeNodeData::find (const T& t) const
+{
+ sal_Int32 l = 0;
+ sal_Int32 r = usageCount() - 1;
+
+ while (l < r)
+ {
+ sal_Int32 const m = ((l + r) >> 1);
+
+ if (t.m_aKey == m_pData[m].m_aKey)
+ return static_cast<sal_uInt16>(m);
+ if (t.m_aKey < m_pData[m].m_aKey)
+ r = m - 1;
+ else
+ l = m + 1;
+ }
+
+ sal_uInt16 const k = static_cast<sal_uInt16>(r);
+ if ((k < capacityCount()) && (t.m_aKey < m_pData[k].m_aKey))
+ return k - 1;
+ else
+ return k;
+}
+
+void OStoreBTreeNodeData::insert (sal_uInt16 i, const T& t)
+{
+ sal_uInt16 const n = usageCount();
+ sal_uInt16 const m = capacityCount();
+ if ((n < m) && (i < m))
+ {
+ // shift right.
+ memmove (&(m_pData[i + 1]), &(m_pData[i]), (n - i) * sizeof(T));
+
+ // insert.
+ m_pData[i] = t;
+ usageCount (n + 1);
+ }
+}
+
+void OStoreBTreeNodeData::remove (sal_uInt16 i)
+{
+ sal_uInt16 const n = usageCount();
+ if (i < n)
+ {
+ // shift left.
+ memmove (&(m_pData[i]), &(m_pData[i + 1]), (n - i - 1) * sizeof(T));
+
+ // truncate.
+ m_pData[n - 1] = T();
+ usageCount (n - 1);
+ }
+}
+
+/*
+ * split (left half copied from right half of left page).
+ */
+void OStoreBTreeNodeData::split (const self& rPageL)
+{
+ sal_uInt16 const h = capacityCount() / 2;
+ memcpy (&(m_pData[0]), &(rPageL.m_pData[h]), h * sizeof(T));
+ truncate (h);
+}
+
+void OStoreBTreeNodeData::truncate (sal_uInt16 n)
+{
+ sal_uInt16 const m = capacityCount();
+ T const t;
+
+ for (sal_uInt16 i = n; i < m; i++)
+ m_pData[i] = t;
+ usageCount (n);
+}
+
+storeError OStoreBTreeNodeObject::guard (sal_uInt32 nAddr)
+{
+ return PageHolderObject< page >::guard (m_xPage, nAddr);
+}
+
+storeError OStoreBTreeNodeObject::verify (sal_uInt32 nAddr) const
+{
+ return PageHolderObject< page >::verify (m_xPage, nAddr);
+}
+
+storeError OStoreBTreeNodeObject::split (
+ sal_uInt16 nIndexL,
+ PageHolderObject< page > & rxPageL,
+ OStorePageBIOS & rBIOS)
+{
+ PageHolderObject< page > xPage (m_xPage);
+ if (!xPage.is())
+ return store_E_InvalidAccess;
+
+ // Check left page usage.
+ if (!rxPageL.is())
+ return store_E_InvalidAccess;
+ if (!rxPageL->querySplit())
+ return store_E_None;
+
+ // Construct right page.
+ PageHolderObject< page > xPageR;
+ if (!xPageR.construct (rBIOS.allocator()))
+ return store_E_OutOfMemory;
+
+ // Split right page off left page.
+ xPageR->split (*rxPageL);
+ xPageR->depth (rxPageL->depth());
+
+ // Allocate right page.
+ self aNodeR (xPageR.get());
+ storeError eErrCode = rBIOS.allocate (aNodeR);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Truncate left page.
+ rxPageL->truncate (rxPageL->capacityCount() / 2);
+
+ // Save left page.
+ self aNodeL (rxPageL.get());
+ eErrCode = rBIOS.saveObjectAt (aNodeL, aNodeL.location());
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Insert right page.
+ OStorePageLink aLink (xPageR->location());
+ xPage->insert (nIndexL + 1, T(xPageR->m_pData[0].m_aKey, aLink));
+
+ // Save this page and leave.
+ return rBIOS.saveObjectAt (*this, location());
+}
+
+/*
+ * remove (down to leaf node, recursive).
+ */
+storeError OStoreBTreeNodeObject::remove (
+ sal_uInt16 nIndexL,
+ OStoreBTreeEntry & rEntryL,
+ OStorePageBIOS & rBIOS)
+{
+ PageHolderObject< page > xImpl (m_xPage);
+ page & rPage = *xImpl;
+
+ // Check depth.
+ storeError eErrCode = store_E_None;
+ if (rPage.depth())
+ {
+ // Check link entry.
+ T const aEntryL (rPage.m_pData[nIndexL]);
+ if (rEntryL.compare (aEntryL) != T::COMPARE_EQUAL)
+ return store_E_InvalidAccess;
+
+ // Load link node.
+ self aNodeL;
+ eErrCode = rBIOS.loadObjectAt (aNodeL, aEntryL.m_aLink.location());
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Recurse: remove from link node.
+ eErrCode = aNodeL.remove (0, rEntryL, rBIOS);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Check resulting link node usage.
+ PageHolderObject< page > xPageL (aNodeL.get());
+ if (xPageL->usageCount() == 0)
+ {
+ // Free empty link node.
+ eErrCode = rBIOS.free (xPageL->location());
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Remove index.
+ rPage.remove (nIndexL);
+ touch();
+ }
+ else
+ {
+
+ // Relink.
+ rPage.m_pData[nIndexL].m_aKey = xPageL->m_pData[0].m_aKey;
+ touch();
+ }
+ }
+ else
+ {
+ // Check leaf entry.
+ if (rEntryL.compare (rPage.m_pData[nIndexL]) != T::COMPARE_EQUAL)
+ return store_E_NotExists;
+
+ // Save leaf entry.
+ rEntryL = rPage.m_pData[nIndexL];
+
+ // Remove leaf index.
+ rPage.remove (nIndexL);
+ touch();
+ }
+
+ // Check for modified node.
+ if (dirty())
+ {
+ // Save this page.
+ eErrCode = rBIOS.saveObjectAt (*this, location());
+ }
+
+ // Done.
+ return eErrCode;
+}
+
+/*
+ * testInvariant.
+ * Precond: root node page loaded.
+ */
+void OStoreBTreeRootObject::testInvariant (char const * message) const
+{
+ OSL_PRECOND(m_xPage != nullptr, "OStoreBTreeRootObject::testInvariant(): Null pointer");
+ SAL_WARN_IF( (m_xPage->location() - m_xPage->size()) != 0, "store", message);
+}
+
+storeError OStoreBTreeRootObject::loadOrCreate (
+ sal_uInt32 nAddr,
+ OStorePageBIOS & rBIOS)
+{
+ storeError eErrCode = rBIOS.loadObjectAt (*this, nAddr);
+ if (eErrCode != store_E_NotExists)
+ return eErrCode;
+
+ eErrCode = construct<page>(rBIOS.allocator());
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ eErrCode = rBIOS.allocate (*this);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Notify caller of the creation.
+ testInvariant("OStoreBTreeRootObject::loadOrCreate(): leave");
+ return store_E_Pending;
+}
+
+storeError OStoreBTreeRootObject::change (
+ PageHolderObject< page > & rxPageL,
+ OStorePageBIOS & rBIOS)
+{
+ PageHolderObject< page > xPage (m_xPage);
+ testInvariant("OStoreBTreeRootObject::change(): enter");
+
+ // Save root location.
+ sal_uInt32 const nRootAddr = xPage->location();
+
+ // Construct new root.
+ if (!rxPageL.construct (rBIOS.allocator()))
+ return store_E_OutOfMemory;
+
+ // Save this as prev root.
+ storeError eErrCode = rBIOS.allocate (*this);
+ if (eErrCode != store_E_None)
+ return store_E_OutOfMemory;
+
+ // Setup new root.
+ rxPageL->depth (xPage->depth() + 1);
+ rxPageL->m_pData[0] = xPage->m_pData[0];
+ rxPageL->m_pData[0].m_aLink = xPage->location();
+ rxPageL->usageCount(1);
+
+ // Change root.
+ rxPageL.swap (xPage);
+ {
+ std::shared_ptr<PageData> tmp (xPage.get());
+ tmp.swap (m_xPage);
+ }
+
+ // Save this as new root and finish.
+ eErrCode = rBIOS.saveObjectAt (*this, nRootAddr);
+ testInvariant("OStoreBTreeRootObject::change(): leave");
+ return eErrCode;
+}
+
+/*
+ * find_lookup (w/o split()).
+ * Precond: root node page loaded.
+ */
+storeError OStoreBTreeRootObject::find_lookup (
+ OStoreBTreeNodeObject & rNode, // [out]
+ sal_uInt16 & rIndex, // [out]
+ OStorePageKey const & rKey,
+ OStorePageBIOS & rBIOS) const
+{
+ // Init node w/ root page.
+ testInvariant("OStoreBTreeRootObject::find_lookup(): enter");
+ {
+ std::shared_ptr<PageData> tmp (m_xPage);
+ tmp.swap (rNode.get());
+ }
+
+ // Setup BTree entry.
+ T const entry (rKey);
+
+ // Check current page.
+ PageHolderObject< page > xPage (rNode.get());
+ for (; xPage->depth() > 0; xPage = rNode.makeHolder< page >())
+ {
+ // Find next page.
+ page const & rPage = *xPage;
+ sal_uInt16 const i = rPage.find(entry);
+ sal_uInt16 const n = rPage.usageCount();
+ if (i >= n)
+ {
+ // Path to entry not exists (Must not happen(?)).
+ return store_E_NotExists;
+ }
+
+ // Check address.
+ sal_uInt32 const nAddr = rPage.m_pData[i].m_aLink.location();
+ if (nAddr == STORE_PAGE_NULL)
+ {
+ // Path to entry not exists (Must not happen(?)).
+ return store_E_NotExists;
+ }
+
+ // Load next page.
+ storeError eErrCode = rBIOS.loadObjectAt (rNode, nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ }
+
+ // Find index.
+ page const & rPage = *xPage;
+ rIndex = rPage.find(entry);
+ if (rIndex >= rPage.usageCount())
+ return store_E_NotExists;
+
+ // Compare entry.
+ T::CompareResult eResult = entry.compare(rPage.m_pData[rIndex]);
+ if (eResult == T::COMPARE_LESS)
+ {
+ SAL_WARN("store", "store::BTreeRoot::find_lookup(): sort error");
+ return store_E_Unknown;
+ }
+
+ // Greater or Equal.
+ testInvariant("OStoreBTreeRootObject::find_lookup(): leave");
+ return store_E_None;
+}
+
+/*
+ * find_insert (possibly with split()).
+ * Precond: root node page loaded.
+ */
+storeError OStoreBTreeRootObject::find_insert (
+ OStoreBTreeNodeObject & rNode, // [out]
+ sal_uInt16 & rIndex, // [out]
+ OStorePageKey const & rKey,
+ OStorePageBIOS & rBIOS)
+{
+ testInvariant("OStoreBTreeRootObject::find_insert(): enter");
+
+ // Check for RootNode split.
+ PageHolderObject< page > xRoot (m_xPage);
+ if (xRoot->querySplit())
+ {
+ PageHolderObject< page > xPageL;
+
+ // Change root.
+ storeError eErrCode = change (xPageL, rBIOS);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Split left page (prev root).
+ eErrCode = split (0, xPageL, rBIOS);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+ }
+
+ // Init node w/ root page.
+ {
+ std::shared_ptr<PageData> tmp (m_xPage);
+ tmp.swap (rNode.get());
+ }
+
+ // Setup BTree entry.
+ T const entry (rKey);
+
+ // Check current Page.
+ PageHolderObject< page > xPage (rNode.get());
+ for (; xPage->depth() > 0; xPage = rNode.makeHolder< page >())
+ {
+ // Find next page.
+ page const & rPage = *xPage;
+ sal_uInt16 const i = rPage.find (entry);
+ sal_uInt16 const n = rPage.usageCount();
+ if (i >= n)
+ {
+ // Path to entry not exists (Must not happen(?)).
+ return store_E_NotExists;
+ }
+
+ // Check address.
+ sal_uInt32 const nAddr = rPage.m_pData[i].m_aLink.location();
+ if (nAddr == STORE_PAGE_NULL)
+ {
+ // Path to entry not exists (Must not happen(?)).
+ return store_E_NotExists;
+ }
+
+ // Load next page.
+ OStoreBTreeNodeObject aNext;
+ storeError eErrCode = rBIOS.loadObjectAt (aNext, nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Check for next node split.
+ PageHolderObject< page > xNext (aNext.get());
+ if (xNext->querySplit())
+ {
+ // Split next node.
+ eErrCode = rNode.split (i, xNext, rBIOS);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Restart.
+ continue;
+ }
+
+ // Let next page be current.
+ std::shared_ptr<PageData> tmp (aNext.get());
+ tmp.swap (rNode.get());
+ }
+
+ // Find index.
+ page const & rPage = *xPage;
+ rIndex = rPage.find(entry);
+ if (rIndex < rPage.usageCount())
+ {
+ // Compare entry.
+ T::CompareResult result = entry.compare (rPage.m_pData[rIndex]);
+ if (result == T::COMPARE_LESS)
+ {
+ SAL_WARN("store", "store::BTreeRoot::find_insert(): sort error");
+ return store_E_Unknown;
+ }
+
+ if (result == T::COMPARE_EQUAL)
+ return store_E_AlreadyExists;
+ }
+
+ // Greater or not (yet) existing.
+ testInvariant("OStoreBTreeRootObject::find_insert(): leave");
+ return store_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/store/source/stortree.hxx b/store/source/stortree.hxx
new file mode 100644
index 0000000000..609d1d1ba8
--- /dev/null
+++ b/store/source/stortree.hxx
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <sal/types.h>
+
+#include <store/types.h>
+
+#include "storbase.hxx"
+
+namespace store
+{
+
+class OStorePageBIOS;
+
+struct OStoreBTreeEntry
+{
+ typedef OStorePageKey K;
+ typedef OStorePageLink L;
+
+ K m_aKey;
+ L m_aLink;
+ sal_uInt32 m_nAttrib;
+
+ explicit OStoreBTreeEntry (
+ K const & rKey = K(),
+ L const & rLink = L())
+ : m_aKey (rKey),
+ m_aLink (rLink),
+ m_nAttrib (store::htonl(0))
+ {}
+
+ enum CompareResult
+ {
+ COMPARE_LESS = -1,
+ COMPARE_EQUAL = 0,
+ COMPARE_GREATER = 1
+ };
+
+ CompareResult compare (const OStoreBTreeEntry& rOther) const
+ {
+ if (m_aKey < rOther.m_aKey)
+ return COMPARE_LESS;
+ else if (m_aKey == rOther.m_aKey)
+ return COMPARE_EQUAL;
+ else
+ return COMPARE_GREATER;
+ }
+};
+
+#define STORE_MAGIC_BTREENODE sal_uInt32(0x58190322)
+
+struct OStoreBTreeNodeData : public store::PageData
+{
+ typedef PageData base;
+ typedef OStoreBTreeNodeData self;
+
+ typedef OStorePageGuard G;
+ typedef OStoreBTreeEntry T;
+
+ G m_aGuard;
+ T m_pData[1];
+
+ static const sal_uInt32 theTypeId = STORE_MAGIC_BTREENODE;
+
+ static const size_t theSize = sizeof(G);
+ static const sal_uInt16 thePageSize = base::theSize + self::theSize;
+ static_assert(STORE_MINIMUM_PAGESIZE >= self::thePageSize, "got to be at least equal in size");
+
+ sal_uInt16 capacity() const
+ {
+ return static_cast<sal_uInt16>(store::ntohs(base::m_aDescr.m_nSize) - self::thePageSize);
+ }
+
+ /** capacityCount (must be even).
+ */
+ sal_uInt16 capacityCount() const
+ {
+ return sal_uInt16(capacity() / sizeof(T));
+ }
+
+ sal_uInt16 usage() const
+ {
+ return static_cast<sal_uInt16>(store::ntohs(base::m_aDescr.m_nUsed) - self::thePageSize);
+ }
+
+ sal_uInt16 usageCount() const
+ {
+ return sal_uInt16(usage() / sizeof(T));
+ }
+ void usageCount (sal_uInt16 nCount)
+ {
+ size_t const nBytes = self::thePageSize + nCount * sizeof(T);
+ base::m_aDescr.m_nUsed = store::htons(sal::static_int_cast< sal_uInt16 >(nBytes));
+ }
+
+ explicit OStoreBTreeNodeData (sal_uInt16 nPageSize);
+
+ void guard()
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, m_pData, capacity());
+ m_aGuard.m_nCRC32 = store::htonl(nCRC32);
+ }
+
+ storeError verify() const
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, m_pData, capacity());
+ if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
+ return store_E_InvalidChecksum;
+ else
+ return store_E_None;
+ }
+
+ sal_uInt32 depth() const
+ {
+ return store::ntohl(self::m_aGuard.m_nMagic);
+ }
+ void depth (sal_uInt32 nDepth)
+ {
+ self::m_aGuard.m_nMagic = store::htonl(nDepth);
+ }
+
+ bool querySplit() const
+ {
+ return usageCount() >= capacityCount();
+ }
+
+ sal_uInt16 find (const T& t) const;
+ void insert (sal_uInt16 i, const T& t);
+ void remove (sal_uInt16 i);
+
+ /** split (left half copied from right half of left page).
+ */
+ void split (const self& rPageL);
+
+ /** truncate (to n elements).
+ */
+ void truncate (sal_uInt16 n);
+};
+
+class OStoreBTreeNodeObject : public store::OStorePageObject
+{
+ typedef OStorePageObject base;
+ typedef OStoreBTreeNodeObject self;
+ typedef OStoreBTreeNodeData page;
+
+ typedef OStoreBTreeEntry T;
+
+public:
+ explicit OStoreBTreeNodeObject (std::shared_ptr<PageData> const & rxPage = std::shared_ptr<PageData>())
+ : OStorePageObject (rxPage)
+ {}
+
+ virtual storeError guard (sal_uInt32 nAddr) override;
+ virtual storeError verify (sal_uInt32 nAddr) const override;
+
+ /** split.
+ *
+ * @param rxPageL [inout] left child to be split
+ */
+ storeError split (
+ sal_uInt16 nIndexL,
+ PageHolderObject< page > & rxPageL,
+ OStorePageBIOS & rBIOS);
+
+ /** remove (down to leaf node, recursive).
+ */
+ storeError remove (
+ sal_uInt16 nIndexL,
+ OStoreBTreeEntry & rEntryL,
+ OStorePageBIOS & rBIOS);
+};
+
+class OStoreBTreeRootObject : public store::OStoreBTreeNodeObject
+{
+ typedef OStoreBTreeNodeObject base;
+ typedef OStoreBTreeNodeData page;
+
+ typedef OStoreBTreeEntry T;
+
+public:
+ explicit OStoreBTreeRootObject (std::shared_ptr<PageData> const & rxPage = std::shared_ptr<PageData>())
+ : OStoreBTreeNodeObject (rxPage)
+ {}
+
+ storeError loadOrCreate (
+ sal_uInt32 nAddr,
+ OStorePageBIOS & rBIOS);
+
+ /** find_lookup (w/o split()).
+ * Precond: root node page loaded.
+ */
+ storeError find_lookup (
+ OStoreBTreeNodeObject & rNode, // [out]
+ sal_uInt16 & rIndex, // [out]
+ OStorePageKey const & rKey,
+ OStorePageBIOS & rBIOS) const;
+
+ /** find_insert (possibly with split()).
+ * Precond: root node page loaded.
+ */
+ storeError find_insert (
+ OStoreBTreeNodeObject & rNode,
+ sal_uInt16 & rIndex,
+ OStorePageKey const & rKey,
+ OStorePageBIOS & rBIOS);
+
+private:
+ /** testInvariant.
+ * Precond: root node page loaded.
+ */
+ void testInvariant (char const * message) const;
+
+ /** change (Root).
+ *
+ * @param rxPageL [out] prev. root (needs split)
+ */
+ storeError change (
+ PageHolderObject< page > & rxPageL,
+ OStorePageBIOS & rBIOS);
+};
+
+} // namespace store
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */