Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / desktop / source / deployment / dp_persmap.cxx
blob63d02f51d41a79f5c14e725dd360e7e5fa92dff4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <dp_misc.h>
21 #include <dp_persmap.h>
22 #include <rtl/byteseq.hxx>
23 #include <rtl/strbuf.hxx>
24 #include <sal/log.hxx>
26 using namespace ::rtl;
28 // the persistent map is used to manage a handful of key-value string pairs
29 // this implementation replaces a rather heavy-weight berkeleydb integration
31 // the file backing up a persistent map consists of line pairs with
32 // - a key string (encoded with chars 0x00..0x0F being escaped)
33 // - a value string (encoded with chars 0x00..0x0F being escaped)
35 namespace dp_misc
38 static const char PmapMagic[4] = {'P','m','p','1'};
40 PersistentMap::PersistentMap( OUString const & url_ )
41 : m_MapFile( expandUnoRcUrl(url_) )
42 , m_bIsOpen( false )
43 , m_bToBeCreated( true )
44 , m_bIsDirty( false )
46 open();
49 PersistentMap::PersistentMap()
50 : m_MapFile( OUString() )
51 , m_bIsOpen( false )
52 , m_bToBeCreated( false )
53 , m_bIsDirty( false )
56 PersistentMap::~PersistentMap()
58 if( m_bIsDirty )
59 flush();
60 if( m_bIsOpen )
61 m_MapFile.close();
65 // replace 0x00..0x0F with "%0".."%F"
66 // replace "%" with "%%"
67 static OString encodeString( const OString& rStr)
69 const sal_Char* pChar = rStr.getStr();
70 const sal_Int32 nLen = rStr.getLength();
71 sal_Int32 i = nLen;
72 // short circuit for the simple non-encoded case
73 while( --i >= 0)
75 const unsigned char c = static_cast<unsigned char>(*(pChar++));
76 if( c <= 0x0F )
77 break;
78 if( c == '%')
79 break;
81 if( i < 0)
82 return rStr;
84 // escape chars 0x00..0x0F with "%0".."%F"
85 OStringBuffer aEncStr( nLen + 32);
86 aEncStr.append( pChar - (nLen-i), nLen - i);
87 while( --i >= 0)
89 unsigned char c = static_cast<unsigned char>(*(pChar++));
90 if( c <= 0x0F )
92 aEncStr.append( '%');
93 c += (c <= 0x09) ? '0' : 'A'-10;
94 } else if( c == '%')
95 aEncStr.append( '%');
96 aEncStr.append( char(c) );
99 return aEncStr.makeStringAndClear();
102 // replace "%0".."%F" with 0x00..0x0F
103 // replace "%%" with "%"
104 static OString decodeString( const sal_Char* pEncChars, int nLen)
106 const char* pChar = pEncChars;
107 sal_Int32 i = nLen;
108 // short circuit for the simple non-encoded case
109 while( --i >= 0)
110 if( *(pChar++) == '%')
111 break;
112 if( i < 0)
113 return OString( pEncChars, nLen);
115 // replace escaped chars with their decoded counterparts
116 OStringBuffer aDecStr( nLen);
117 pChar = pEncChars;
118 for( i = nLen; --i >= 0;)
120 sal_Char c = *(pChar++);
121 // handle escaped character
122 if( c == '%')
124 --i;
125 OSL_ASSERT( i >= 0);
126 c = *(pChar++);
127 if( ('0' <= c) && (c <= '9'))
128 c -= '0';
129 else
131 OSL_ASSERT( ('A' <= c) && (c <= 'F'));
132 c -= ('A'-10);
135 aDecStr.append( c);
138 return aDecStr.makeStringAndClear();
141 void PersistentMap::open()
143 // open the existing file
144 sal_uInt32 const nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
146 const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
147 m_bIsOpen = (rcOpen == osl::File::E_None);
149 // or create later if needed
150 m_bToBeCreated &= (rcOpen == osl::File::E_NOENT) && !m_bIsOpen;
152 if( !m_bIsOpen)
153 return;
155 readAll();
159 void PersistentMap::readAll()
161 // prepare for re-reading the map-file
162 m_entries.clear();
163 const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
164 if (nRes != osl::FileBase::E_None)
166 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
167 return;
170 // read header and check magic
171 char aHeaderBytes[ sizeof(PmapMagic)];
172 sal_uInt64 nBytesRead = 0;
173 m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead);
174 OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes));
175 if( nBytesRead != sizeof(aHeaderBytes))
176 return;
177 // check header magic
178 for( int i = 0; i < int(sizeof(PmapMagic)); ++i)
179 if( aHeaderBytes[i] != PmapMagic[i])
180 return;
182 // read key value pairs and add them to the map
183 ByteSequence aKeyLine;
184 ByteSequence aValLine;
185 for(;;)
187 // read key-value line pair
188 // an empty key name indicates the end of the line pairs
189 if( m_MapFile.readLine( aKeyLine) != osl::File::E_None)
190 return;
191 if( !aKeyLine.getLength())
192 break;
193 if( m_MapFile.readLine( aValLine) != osl::File::E_None)
194 return;
195 // decode key and value strings
196 const OString aKeyName = decodeString( reinterpret_cast<char const *>(aKeyLine.getConstArray()), aKeyLine.getLength());
197 const OString aValName = decodeString( reinterpret_cast<char const *>(aValLine.getConstArray()), aValLine.getLength());
198 // insert key-value pair into map
199 add( aKeyName, aValName );
200 // check end-of-file status
201 sal_Bool bIsEOF = true;
202 if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None )
203 return;
204 if( bIsEOF )
205 break;
208 m_bIsDirty = false;
211 void PersistentMap::flush()
213 if( !m_bIsDirty)
214 return;
215 if( m_bToBeCreated && !m_entries.empty())
217 const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create;
218 const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
219 m_bIsOpen = (rcOpen == osl::File::E_None);
220 m_bToBeCreated = !m_bIsOpen;
222 if( !m_bIsOpen)
223 return;
225 // write header magic
226 const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
227 if (nRes != osl::FileBase::E_None)
229 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
230 return;
232 sal_uInt64 nBytesWritten = 0;
233 m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten);
235 // write key value pairs
236 for (auto const& entry : m_entries)
238 // write line for key
239 const OString aKeyString = encodeString( entry.first);
240 const sal_Int32 nKeyLen = aKeyString.getLength();
241 m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten);
242 OSL_ASSERT( nKeyLen == static_cast<sal_Int32>(nBytesWritten));
243 m_MapFile.write( "\n", 1, nBytesWritten);
244 // write line for value
245 const OString& rValString = encodeString( entry.second);
246 const sal_Int32 nValLen = rValString.getLength();
247 m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten);
248 OSL_ASSERT( nValLen == static_cast<sal_Int32>(nBytesWritten));
249 m_MapFile.write( "\n", 1, nBytesWritten);
252 // write a file delimiter (an empty key-string)
253 m_MapFile.write( "\n", 1, nBytesWritten);
254 // truncate file here
255 sal_uInt64 nNewFileSize;
256 if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None)
257 m_MapFile.setSize( nNewFileSize);
258 // flush to disk
259 m_MapFile.sync();
260 // the in-memory map now matches to the file on disk
261 m_bIsDirty = false;
264 bool PersistentMap::has( OString const & key ) const
266 return get( nullptr, key );
269 bool PersistentMap::get( OString * value, OString const & key ) const
271 t_string2string_map::const_iterator it = m_entries.find( key);
272 if( it == m_entries.end())
273 return false;
274 if( value)
275 *value = it->second;
276 return true;
279 void PersistentMap::add( OString const & key, OString const & value )
281 auto r = m_entries.emplace(key,value);
282 m_bIsDirty = r.second;
286 void PersistentMap::put( OString const & key, OString const & value )
288 add( key, value);
289 // HACK: flush now as the extension manager does not seem
290 // to properly destruct this object in some situations
291 if(m_bIsDirty)
292 flush();
295 bool PersistentMap::erase( OString const & key )
297 size_t nCount = m_entries.erase( key);
298 if( !nCount)
299 return false;
300 m_bIsDirty = true;
301 flush();
302 return true;
307 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */