diff options
Diffstat (limited to 'store/source')
-rw-r--r-- | store/source/lockbyte.cxx | 886 | ||||
-rw-r--r-- | store/source/lockbyte.hxx | 154 | ||||
-rw-r--r-- | store/source/object.cxx | 32 | ||||
-rw-r--r-- | store/source/object.hxx | 54 | ||||
-rw-r--r-- | store/source/storbase.cxx | 124 | ||||
-rw-r--r-- | store/source/storbase.hxx | 608 | ||||
-rw-r--r-- | store/source/storbios.cxx | 890 | ||||
-rw-r--r-- | store/source/storbios.hxx | 170 | ||||
-rw-r--r-- | store/source/storcach.cxx | 414 | ||||
-rw-r--r-- | store/source/storcach.hxx | 99 | ||||
-rw-r--r-- | store/source/stordata.cxx | 1043 | ||||
-rw-r--r-- | store/source/stordata.hxx | 748 | ||||
-rw-r--r-- | store/source/stordir.cxx | 184 | ||||
-rw-r--r-- | store/source/stordir.hxx | 104 | ||||
-rw-r--r-- | store/source/store.cxx | 371 | ||||
-rw-r--r-- | store/source/storlckb.cxx | 387 | ||||
-rw-r--r-- | store/source/storlckb.hxx | 131 | ||||
-rw-r--r-- | store/source/storpage.cxx | 494 | ||||
-rw-r--r-- | store/source/storpage.hxx | 156 | ||||
-rw-r--r-- | store/source/stortree.cxx | 507 | ||||
-rw-r--r-- | store/source/stortree.hxx | 248 |
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: */ |