diff options
Diffstat (limited to '')
-rw-r--r-- | desktop/source/deployment/dp_persmap.cxx | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/desktop/source/deployment/dp_persmap.cxx b/desktop/source/deployment/dp_persmap.cxx new file mode 100644 index 000000000..8b032fffb --- /dev/null +++ b/desktop/source/deployment/dp_persmap.cxx @@ -0,0 +1,312 @@ +/* -*- 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 <cstddef> + +#include <dp_misc.h> +#include <dp_persmap.h> +#include <o3tl/safeint.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/strbuf.hxx> +#include <sal/log.hxx> + +using namespace ::rtl; + +// the persistent map is used to manage a handful of key-value string pairs +// this implementation replaces a rather heavy-weight berkeleydb integration + +// the file backing up a persistent map consists of line pairs with +// - a key string (encoded with chars 0x00..0x0F being escaped) +// - a value string (encoded with chars 0x00..0x0F being escaped) + +namespace dp_misc +{ + +const char PmapMagic[4] = {'P','m','p','1'}; + +PersistentMap::PersistentMap( OUString const & url_ ) +: m_MapFile( expandUnoRcUrl(url_) ) +, m_bIsOpen( false ) +, m_bToBeCreated( true ) +, m_bIsDirty( false ) +{ + open(); +} + +PersistentMap::PersistentMap() +: m_MapFile( OUString() ) +, m_bIsOpen( false ) +, m_bToBeCreated( false ) +, m_bIsDirty( false ) +{} + +PersistentMap::~PersistentMap() +{ + if( m_bIsDirty ) + flush(); + if( m_bIsOpen ) + m_MapFile.close(); +} + + +// replace 0x00..0x0F with "%0".."%F" +// replace "%" with "%%" +static OString encodeString( const OString& rStr) +{ + const char* pChar = rStr.getStr(); + const sal_Int32 nLen = rStr.getLength(); + sal_Int32 i = nLen; + // short circuit for the simple non-encoded case + while( --i >= 0) + { + const unsigned char c = static_cast<unsigned char>(*(pChar++)); + if( c <= 0x0F ) + break; + if( c == '%') + break; + } + if( i < 0) + return rStr; + + // escape chars 0x00..0x0F with "%0".."%F" + OStringBuffer aEncStr( nLen + 32); + aEncStr.append( pChar - (nLen-i), nLen - i); + while( --i >= 0) + { + unsigned char c = static_cast<unsigned char>(*(pChar++)); + if( c <= 0x0F ) + { + aEncStr.append( '%'); + c += (c <= 0x09) ? '0' : 'A'-10; + } else if( c == '%') + aEncStr.append( '%'); + aEncStr.append( char(c) ); + } + + return aEncStr.makeStringAndClear(); +} + +// replace "%0".."%F" with 0x00..0x0F +// replace "%%" with "%" +static OString decodeString( const char* pEncChars, int nLen) +{ + const char* pChar = pEncChars; + sal_Int32 i = nLen; + // short circuit for the simple non-encoded case + while( --i >= 0) + if( *(pChar++) == '%') + break; + if( i < 0) + return OString( pEncChars, nLen); + + // replace escaped chars with their decoded counterparts + OStringBuffer aDecStr( nLen); + pChar = pEncChars; + for( i = nLen; --i >= 0;) + { + char c = *(pChar++); + // handle escaped character + if( c == '%') + { + --i; + OSL_ASSERT( i >= 0); + c = *(pChar++); + if( ('0' <= c) && (c <= '9')) + c -= '0'; + else + { + OSL_ASSERT( ('A' <= c) && (c <= 'F')); + c -= ('A'-10); + } + } + aDecStr.append( c); + } + + return aDecStr.makeStringAndClear(); +} + +void PersistentMap::open() +{ + // open the existing file + sal_uInt32 const nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write; + + const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags); + m_bIsOpen = (rcOpen == osl::File::E_None); + + // or create later if needed + m_bToBeCreated &= (rcOpen == osl::File::E_NOENT) && !m_bIsOpen; + + if( !m_bIsOpen) + return; + + readAll(); +} + + +void PersistentMap::readAll() +{ + // prepare for re-reading the map-file + m_entries.clear(); + const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0); + if (nRes != osl::FileBase::E_None) + { + SAL_WARN("desktop.deployment", "setPos failed with " << +nRes); + return; + } + + // read header and check magic + char aHeaderBytes[ sizeof(PmapMagic)]; + sal_uInt64 nBytesRead = 0; + m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead); + OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes)); + if( nBytesRead != sizeof(aHeaderBytes)) + return; + // check header magic + for( std::size_t i = 0; i < sizeof(PmapMagic); ++i) + if( aHeaderBytes[i] != PmapMagic[i]) + return; + + // read key value pairs and add them to the map + ByteSequence aKeyLine; + ByteSequence aValLine; + for(;;) + { + // read key-value line pair + // an empty key name indicates the end of the line pairs + if( m_MapFile.readLine( aKeyLine) != osl::File::E_None) + return; + if( !aKeyLine.getLength()) + break; + if( m_MapFile.readLine( aValLine) != osl::File::E_None) + return; + // decode key and value strings + const OString aKeyName = decodeString( reinterpret_cast<char const *>(aKeyLine.getConstArray()), aKeyLine.getLength()); + const OString aValName = decodeString( reinterpret_cast<char const *>(aValLine.getConstArray()), aValLine.getLength()); + // insert key-value pair into map + add( aKeyName, aValName ); + // check end-of-file status + sal_Bool bIsEOF = true; + if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None ) + return; + if( bIsEOF ) + break; + } + + m_bIsDirty = false; +} + +void PersistentMap::flush() +{ + if( !m_bIsDirty) + return; + if( m_bToBeCreated && !m_entries.empty()) + { + const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create; + const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags); + m_bIsOpen = (rcOpen == osl::File::E_None); + m_bToBeCreated = !m_bIsOpen; + } + if( !m_bIsOpen) + return; + + // write header magic + const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0); + if (nRes != osl::FileBase::E_None) + { + SAL_WARN("desktop.deployment", "setPos failed with " << +nRes); + return; + } + sal_uInt64 nBytesWritten = 0; + m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten); + + // write key value pairs + for (auto const& entry : m_entries) + { + // write line for key + const OString aKeyString = encodeString( entry.first); + const sal_Int32 nKeyLen = aKeyString.getLength(); + m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten); + OSL_ASSERT( o3tl::make_unsigned(nKeyLen) == nBytesWritten); + m_MapFile.write( "\n", 1, nBytesWritten); + // write line for value + const OString& rValString = encodeString( entry.second); + const sal_Int32 nValLen = rValString.getLength(); + m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten); + OSL_ASSERT( o3tl::make_unsigned(nValLen) == nBytesWritten); + m_MapFile.write( "\n", 1, nBytesWritten); + } + + // write a file delimiter (an empty key-string) + m_MapFile.write( "\n", 1, nBytesWritten); + // truncate file here + sal_uInt64 nNewFileSize; + if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None) + m_MapFile.setSize( nNewFileSize); + // flush to disk + m_MapFile.sync(); + // the in-memory map now matches to the file on disk + m_bIsDirty = false; +} + +bool PersistentMap::has( OString const & key ) const +{ + return get( nullptr, key ); +} + +bool PersistentMap::get( OString * value, OString const & key ) const +{ + t_string2string_map::const_iterator it = m_entries.find( key); + if( it == m_entries.end()) + return false; + if( value) + *value = it->second; + return true; +} + +void PersistentMap::add( OString const & key, OString const & value ) +{ + auto r = m_entries.emplace(key,value); + m_bIsDirty = r.second; +} + + +void PersistentMap::put( OString const & key, OString const & value ) +{ + add( key, value); + // HACK: flush now as the extension manager does not seem + // to properly destruct this object in some situations + if(m_bIsDirty) + flush(); +} + +bool PersistentMap::erase( OString const & key ) +{ + size_t nCount = m_entries.erase( key); + if( !nCount) + return false; + m_bIsDirty = true; + flush(); + return true; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |