LibreOffice Module desktop (master) 1
dp_persmap.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <cstddef>
23
24#include <dp_misc.h>
25#include <dp_persmap.h>
26#include <o3tl/safeint.hxx>
27#include <rtl/byteseq.hxx>
28#include <rtl/strbuf.hxx>
29#include <sal/log.hxx>
30
31using namespace ::rtl;
32
33// the persistent map is used to manage a handful of key-value string pairs
34// this implementation replaces a rather heavy-weight berkeleydb integration
35
36// the file backing up a persistent map consists of line pairs with
37// - a key string (encoded with chars 0x00..0x0F being escaped)
38// - a value string (encoded with chars 0x00..0x0F being escaped)
39
40namespace dp_misc
41{
42
43const char PmapMagic[4] = {'P','m','p','1'};
44
45PersistentMap::PersistentMap( OUString const & url_ )
46: m_MapFile( expandUnoRcUrl(url_) )
47, m_bIsOpen( false )
48, m_bToBeCreated( true )
49, m_bIsDirty( false )
50{
51 open();
52}
53
55: m_MapFile( OUString() )
56, m_bIsOpen( false )
57, m_bToBeCreated( false )
58, m_bIsDirty( false )
59{}
60
62{
63 if( m_bIsDirty )
64 flush();
65 if( m_bIsOpen )
66 m_MapFile.close();
67}
68
69
70// replace 0x00..0x0F with "%0".."%F"
71// replace "%" with "%%"
72static OString encodeString( const OString& rStr)
73{
74 const char* pChar = rStr.getStr();
75 const sal_Int32 nLen = rStr.getLength();
76 sal_Int32 i = nLen;
77 // short circuit for the simple non-encoded case
78 while( --i >= 0)
79 {
80 const unsigned char c = static_cast<unsigned char>(*(pChar++));
81 if( c <= 0x0F )
82 break;
83 if( c == '%')
84 break;
85 }
86 if( i < 0)
87 return rStr;
88
89 // escape chars 0x00..0x0F with "%0".."%F"
90 OStringBuffer aEncStr( nLen + 32);
91 aEncStr.append( pChar - (nLen-i), nLen - i);
92 while( --i >= 0)
93 {
94 unsigned char c = static_cast<unsigned char>(*(pChar++));
95 if( c <= 0x0F )
96 {
97 aEncStr.append( '%');
98 c += (c <= 0x09) ? '0' : 'A'-10;
99 } else if( c == '%')
100 aEncStr.append( '%');
101 aEncStr.append( char(c) );
102 }
103
104 return aEncStr.makeStringAndClear();
105}
106
107// replace "%0".."%F" with 0x00..0x0F
108// replace "%%" with "%"
109static OString decodeString( const char* pEncChars, int nLen)
110{
111 const char* pChar = pEncChars;
112 sal_Int32 i = nLen;
113 // short circuit for the simple non-encoded case
114 while( --i >= 0)
115 if( *(pChar++) == '%')
116 break;
117 if( i < 0)
118 return OString( pEncChars, nLen);
119
120 // replace escaped chars with their decoded counterparts
121 OStringBuffer aDecStr( nLen);
122 pChar = pEncChars;
123 for( i = nLen; --i >= 0;)
124 {
125 char c = *(pChar++);
126 // handle escaped character
127 if( c == '%')
128 {
129 --i;
130 OSL_ASSERT( i >= 0);
131 c = *(pChar++);
132 if( ('0' <= c) && (c <= '9'))
133 c -= '0';
134 else
135 {
136 OSL_ASSERT( ('A' <= c) && (c <= 'F'));
137 c -= ('A'-10);
138 }
139 }
140 aDecStr.append( c);
141 }
142
143 return aDecStr.makeStringAndClear();
144}
145
147{
148 // open the existing file
149 sal_uInt32 const nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
150
151 const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
152 m_bIsOpen = (rcOpen == osl::File::E_None);
153
154 // or create later if needed
155 m_bToBeCreated &= (rcOpen == osl::File::E_NOENT) && !m_bIsOpen;
156
157 if( !m_bIsOpen)
158 return;
159
160 readAll();
161}
162
163
165{
166 // prepare for re-reading the map-file
167 m_entries.clear();
168 const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
169 if (nRes != osl::FileBase::E_None)
170 {
171 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
172 return;
173 }
174
175 // read header and check magic
176 char aHeaderBytes[ sizeof(PmapMagic)];
177 sal_uInt64 nBytesRead = 0;
178 m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead);
179 OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes));
180 if( nBytesRead != sizeof(aHeaderBytes))
181 return;
182 // check header magic
183 for( std::size_t i = 0; i < sizeof(PmapMagic); ++i)
184 if( aHeaderBytes[i] != PmapMagic[i])
185 return;
186
187 // read key value pairs and add them to the map
188 ByteSequence aKeyLine;
189 ByteSequence aValLine;
190 for(;;)
191 {
192 // read key-value line pair
193 // an empty key name indicates the end of the line pairs
194 if( m_MapFile.readLine( aKeyLine) != osl::File::E_None)
195 return;
196 if( !aKeyLine.getLength())
197 break;
198 if( m_MapFile.readLine( aValLine) != osl::File::E_None)
199 return;
200 // decode key and value strings
201 const OString aKeyName = decodeString( reinterpret_cast<char const *>(aKeyLine.getConstArray()), aKeyLine.getLength());
202 const OString aValName = decodeString( reinterpret_cast<char const *>(aValLine.getConstArray()), aValLine.getLength());
203 // insert key-value pair into map
204 add( aKeyName, aValName );
205 // check end-of-file status
206 sal_Bool bIsEOF = true;
207 if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None )
208 return;
209 if( bIsEOF )
210 break;
211 }
212
213 m_bIsDirty = false;
214}
215
217{
218 if( !m_bIsDirty)
219 return;
220 if( m_bToBeCreated && !m_entries.empty())
221 {
222 const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create;
223 const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
224 m_bIsOpen = (rcOpen == osl::File::E_None);
226 }
227 if( !m_bIsOpen)
228 return;
229
230 // write header magic
231 const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
232 if (nRes != osl::FileBase::E_None)
233 {
234 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
235 return;
236 }
237 sal_uInt64 nBytesWritten = 0;
238 m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten);
239
240 // write key value pairs
241 for (auto const& entry : m_entries)
242 {
243 // write line for key
244 const OString aKeyString = encodeString( entry.first);
245 const sal_Int32 nKeyLen = aKeyString.getLength();
246 m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten);
247 OSL_ASSERT( o3tl::make_unsigned(nKeyLen) == nBytesWritten);
248 m_MapFile.write( "\n", 1, nBytesWritten);
249 // write line for value
250 const OString& rValString = encodeString( entry.second);
251 const sal_Int32 nValLen = rValString.getLength();
252 m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten);
253 OSL_ASSERT( o3tl::make_unsigned(nValLen) == nBytesWritten);
254 m_MapFile.write( "\n", 1, nBytesWritten);
255 }
256
257 // write a file delimiter (an empty key-string)
258 m_MapFile.write( "\n", 1, nBytesWritten);
259 // truncate file here
260 sal_uInt64 nNewFileSize;
261 if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None)
262 m_MapFile.setSize( nNewFileSize);
263 // flush to disk
264 m_MapFile.sync();
265 // the in-memory map now matches to the file on disk
266 m_bIsDirty = false;
267}
268
269bool PersistentMap::has( OString const & key ) const
270{
271 return get( nullptr, key );
272}
273
274bool PersistentMap::get( OString * value, OString const & key ) const
275{
276 t_string2string_map::const_iterator it = m_entries.find( key);
277 if( it == m_entries.end())
278 return false;
279 if( value)
280 *value = it->second;
281 return true;
282}
283
284void PersistentMap::add( OString const & key, OString const & value )
285{
286 auto r = m_entries.emplace(key,value);
287 m_bIsDirty = r.second;
288}
289
290
291void PersistentMap::put( OString const & key, OString const & value )
292{
293 add( key, value);
294 // HACK: flush now as the extension manager does not seem
295 // to properly destruct this object in some situations
296 if(m_bIsDirty)
297 flush();
298}
299
300bool PersistentMap::erase( OString const & key )
301{
302 size_t nCount = m_entries.erase( key);
303 if( !nCount)
304 return false;
305 m_bIsDirty = true;
306 flush();
307 return true;
308}
309
310}
311
312/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool get(OString *value, OString const &key) const
Definition: dp_persmap.cxx:274
void put(OString const &key, OString const &value)
Definition: dp_persmap.cxx:291
bool has(OString const &key) const
Definition: dp_persmap.cxx:269
PersistentMap()
in mem db
Definition: dp_persmap.cxx:54
bool erase(OString const &key)
Definition: dp_persmap.cxx:300
void add(OString const &key, OString const &value)
Definition: dp_persmap.cxx:284
t_string2string_map m_entries
Definition: dp_persmap.h:35
::osl::File m_MapFile
Definition: dp_persmap.h:34
Any value
OUString url_
int nCount
#define SAL_WARN(area, stream)
static OString encodeString(const OString &rStr)
Definition: dp_persmap.cxx:72
OUString expandUnoRcUrl(OUString const &url)
Definition: dp_misc.cxx:316
const char PmapMagic[4]
Definition: dp_persmap.cxx:43
static OString decodeString(const char *pEncChars, int nLen)
Definition: dp_persmap.cxx:109
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
unsigned char sal_Bool
const char * pChar