tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / desktop / source / deployment / dp_persmap.cxx
blob9cc74b0c79a8959202cfcf969721693c1d46aa9f
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 <sal/config.h>
22 #include <cstddef>
24 #include <dp_misc.h>
25 #include <dp_persmap.h>
26 #include <osl/diagnose.h>
27 #include <o3tl/safeint.hxx>
28 #include <rtl/byteseq.hxx>
29 #include <rtl/strbuf.hxx>
30 #include <sal/log.hxx>
32 using namespace ::rtl;
34 // the persistent map is used to manage a handful of key-value string pairs
35 // this implementation replaces a rather heavy-weight berkeleydb integration
37 // the file backing up a persistent map consists of line pairs with
38 // - a key string (encoded with chars 0x00..0x0F being escaped)
39 // - a value string (encoded with chars 0x00..0x0F being escaped)
41 namespace dp_misc
44 const char PmapMagic[4] = {'P','m','p','1'};
46 PersistentMap::PersistentMap( OUString const & url_ )
47 : m_MapFile( expandUnoRcUrl(url_) )
48 , m_bIsOpen( false )
49 , m_bToBeCreated( true )
50 , m_bIsDirty( false )
52 open();
55 PersistentMap::PersistentMap()
56 : m_MapFile( OUString() )
57 , m_bIsOpen( false )
58 , m_bToBeCreated( false )
59 , m_bIsDirty( false )
62 PersistentMap::~PersistentMap()
64 if( m_bIsDirty )
65 flush();
66 if( m_bIsOpen )
67 m_MapFile.close();
71 // replace 0x00..0x0F with "%0".."%F"
72 // replace "%" with "%%"
73 static OString encodeString( const OString& rStr)
75 const char* pChar = rStr.getStr();
76 const sal_Int32 nLen = rStr.getLength();
77 sal_Int32 i = nLen;
78 // short circuit for the simple non-encoded case
79 while( --i >= 0)
81 const unsigned char c = static_cast<unsigned char>(*(pChar++));
82 if( c <= 0x0F )
83 break;
84 if( c == '%')
85 break;
87 if( i < 0)
88 return rStr;
90 // escape chars 0x00..0x0F with "%0".."%F"
91 OStringBuffer aEncStr( nLen + 32);
92 aEncStr.append( pChar - (nLen-i), nLen - i);
93 while( --i >= 0)
95 unsigned char c = static_cast<unsigned char>(*(pChar++));
96 if( c <= 0x0F )
98 aEncStr.append( '%');
99 c += (c <= 0x09) ? '0' : 'A'-10;
100 } else if( c == '%')
101 aEncStr.append( '%');
102 aEncStr.append( char(c) );
105 return aEncStr.makeStringAndClear();
108 // replace "%0".."%F" with 0x00..0x0F
109 // replace "%%" with "%"
110 static OString decodeString( const char* pEncChars, int nLen)
112 const char* pChar = pEncChars;
113 sal_Int32 i = nLen;
114 // short circuit for the simple non-encoded case
115 while( --i >= 0)
116 if( *(pChar++) == '%')
117 break;
118 if( i < 0)
119 return OString( pEncChars, nLen);
121 // replace escaped chars with their decoded counterparts
122 OStringBuffer aDecStr( nLen);
123 pChar = pEncChars;
124 for( i = nLen; --i >= 0;)
126 char c = *(pChar++);
127 // handle escaped character
128 if( c == '%')
130 --i;
131 OSL_ASSERT( i >= 0);
132 c = *(pChar++);
133 if( ('0' <= c) && (c <= '9'))
134 c -= '0';
135 else
137 OSL_ASSERT( ('A' <= c) && (c <= 'F'));
138 c -= ('A'-10);
141 aDecStr.append( c);
144 return aDecStr.makeStringAndClear();
147 void PersistentMap::open()
149 // open the existing file
150 sal_uInt32 const nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
152 const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
153 m_bIsOpen = (rcOpen == osl::File::E_None);
155 // or create later if needed
156 m_bToBeCreated &= (rcOpen == osl::File::E_NOENT) && !m_bIsOpen;
158 if( !m_bIsOpen)
159 return;
161 readAll();
165 void PersistentMap::readAll()
167 // prepare for re-reading the map-file
168 m_entries.clear();
169 const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
170 if (nRes != osl::FileBase::E_None)
172 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
173 return;
176 // read header and check magic
177 char aHeaderBytes[ sizeof(PmapMagic)];
178 sal_uInt64 nBytesRead = 0;
179 m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead);
180 OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes));
181 if( nBytesRead != sizeof(aHeaderBytes))
182 return;
183 // check header magic
184 for( std::size_t i = 0; i < sizeof(PmapMagic); ++i)
185 if( aHeaderBytes[i] != PmapMagic[i])
186 return;
188 // read key value pairs and add them to the map
189 ByteSequence aKeyLine;
190 ByteSequence aValLine;
191 for(;;)
193 // read key-value line pair
194 // an empty key name indicates the end of the line pairs
195 if( m_MapFile.readLine( aKeyLine) != osl::File::E_None)
196 return;
197 if( !aKeyLine.getLength())
198 break;
199 if( m_MapFile.readLine( aValLine) != osl::File::E_None)
200 return;
201 // decode key and value strings
202 const OString aKeyName = decodeString( reinterpret_cast<char const *>(aKeyLine.getConstArray()), aKeyLine.getLength());
203 const OString aValName = decodeString( reinterpret_cast<char const *>(aValLine.getConstArray()), aValLine.getLength());
204 // insert key-value pair into map
205 add( aKeyName, aValName );
206 // check end-of-file status
207 sal_Bool bIsEOF = true;
208 if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None )
209 return;
210 if( bIsEOF )
211 break;
214 m_bIsDirty = false;
217 void PersistentMap::flush()
219 if( !m_bIsDirty)
220 return;
221 if( m_bToBeCreated && !m_entries.empty())
223 const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create;
224 const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
225 m_bIsOpen = (rcOpen == osl::File::E_None);
226 m_bToBeCreated = !m_bIsOpen;
228 if( !m_bIsOpen)
229 return;
231 // write header magic
232 const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
233 if (nRes != osl::FileBase::E_None)
235 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
236 return;
238 sal_uInt64 nBytesWritten = 0;
239 m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten);
241 // write key value pairs
242 for (auto const& entry : m_entries)
244 // write line for key
245 const OString aKeyString = encodeString( entry.first);
246 const sal_Int32 nKeyLen = aKeyString.getLength();
247 m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten);
248 OSL_ASSERT( o3tl::make_unsigned(nKeyLen) == nBytesWritten);
249 m_MapFile.write( "\n", 1, nBytesWritten);
250 // write line for value
251 const OString aValString = encodeString( entry.second);
252 const sal_Int32 nValLen = aValString.getLength();
253 m_MapFile.write( aValString.getStr(), nValLen, nBytesWritten);
254 OSL_ASSERT( o3tl::make_unsigned(nValLen) == nBytesWritten);
255 m_MapFile.write( "\n", 1, nBytesWritten);
258 // write a file delimiter (an empty key-string)
259 m_MapFile.write( "\n", 1, nBytesWritten);
260 // truncate file here
261 sal_uInt64 nNewFileSize;
262 if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None)
263 m_MapFile.setSize( nNewFileSize);
264 // flush to disk
265 m_MapFile.sync();
266 // the in-memory map now matches to the file on disk
267 m_bIsDirty = false;
270 bool PersistentMap::has( OString const & key ) const
272 return get( nullptr, key );
275 bool PersistentMap::get( OString * value, OString const & key ) const
277 t_string2string_map::const_iterator it = m_entries.find( key);
278 if( it == m_entries.end())
279 return false;
280 if( value)
281 *value = it->second;
282 return true;
285 void PersistentMap::add( OString const & key, OString const & value )
287 auto r = m_entries.emplace(key,value);
288 m_bIsDirty = r.second;
292 void PersistentMap::put( OString const & key, OString const & value )
294 add( key, value);
295 // HACK: flush now as the extension manager does not seem
296 // to properly destruct this object in some situations
297 if(m_bIsDirty)
298 flush();
301 bool PersistentMap::erase( OString const & key )
303 size_t nCount = m_entries.erase( key);
304 if( !nCount)
305 return false;
306 m_bIsDirty = true;
307 flush();
308 return true;
313 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */