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 <dp_misc.h>
21 #include <dp_persmap.h>
22 #include <rtl/byteseq.hxx>
23 #include <rtl/strbuf.hxx>
24 #include <sal/log.hxx>
25 
26 using namespace ::rtl;
27 
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
30 
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)
34 
35 namespace dp_misc
36 {
37 
38 static const char PmapMagic[4] = {'P','m','p','1'};
39 
40 PersistentMap::PersistentMap( OUString const & url_ )
41 : m_MapFile( expandUnoRcUrl(url_) )
42 , m_bIsOpen( false )
43 , m_bToBeCreated( true )
44 , m_bIsDirty( false )
45 {
46  open();
47 }
48 
50 : m_MapFile( OUString() )
51 , m_bIsOpen( false )
52 , m_bToBeCreated( false )
53 , m_bIsDirty( false )
54 {}
55 
57 {
58  if( m_bIsDirty )
59  flush();
60  if( m_bIsOpen )
61  m_MapFile.close();
62 }
63 
64 
65 // replace 0x00..0x0F with "%0".."%F"
66 // replace "%" with "%%"
67 static OString encodeString( const OString& rStr)
68 {
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)
74  {
75  const unsigned char c = static_cast<unsigned char>(*(pChar++));
76  if( c <= 0x0F )
77  break;
78  if( c == '%')
79  break;
80  }
81  if( i < 0)
82  return rStr;
83 
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)
88  {
89  unsigned char c = static_cast<unsigned char>(*(pChar++));
90  if( c <= 0x0F )
91  {
92  aEncStr.append( '%');
93  c += (c <= 0x09) ? '0' : 'A'-10;
94  } else if( c == '%')
95  aEncStr.append( '%');
96  aEncStr.append( char(c) );
97  }
98 
99  return aEncStr.makeStringAndClear();
100 }
101 
102 // replace "%0".."%F" with 0x00..0x0F
103 // replace "%%" with "%"
104 static OString decodeString( const sal_Char* pEncChars, int nLen)
105 {
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);
114 
115  // replace escaped chars with their decoded counterparts
116  OStringBuffer aDecStr( nLen);
117  pChar = pEncChars;
118  for( i = nLen; --i >= 0;)
119  {
120  sal_Char c = *(pChar++);
121  // handle escaped character
122  if( c == '%')
123  {
124  --i;
125  OSL_ASSERT( i >= 0);
126  c = *(pChar++);
127  if( ('0' <= c) && (c <= '9'))
128  c -= '0';
129  else
130  {
131  OSL_ASSERT( ('A' <= c) && (c <= 'F'));
132  c -= ('A'-10);
133  }
134  }
135  aDecStr.append( c);
136  }
137 
138  return aDecStr.makeStringAndClear();
139 }
140 
142 {
143  // open the existing file
144  sal_uInt32 const nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
145 
146  const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
147  m_bIsOpen = (rcOpen == osl::File::E_None);
148 
149  // or create later if needed
150  m_bToBeCreated &= (rcOpen == osl::File::E_NOENT) && !m_bIsOpen;
151 
152  if( !m_bIsOpen)
153  return;
154 
155  readAll();
156 }
157 
158 
160 {
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)
165  {
166  SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
167  return;
168  }
169 
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;
181 
182  // read key value pairs and add them to the map
183  ByteSequence aKeyLine;
184  ByteSequence aValLine;
185  for(;;)
186  {
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;
206  }
207 
208  m_bIsDirty = false;
209 }
210 
212 {
213  if( !m_bIsDirty)
214  return;
215  if( m_bToBeCreated && !m_entries.empty())
216  {
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);
221  }
222  if( !m_bIsOpen)
223  return;
224 
225  // write header magic
226  const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
227  if (nRes != osl::FileBase::E_None)
228  {
229  SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
230  return;
231  }
232  sal_uInt64 nBytesWritten = 0;
233  m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten);
234 
235  // write key value pairs
236  for (auto const& entry : m_entries)
237  {
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);
250  }
251 
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;
262 }
263 
264 bool PersistentMap::has( OString const & key ) const
265 {
266  return get( nullptr, key );
267 }
268 
269 bool PersistentMap::get( OString * value, OString const & key ) const
270 {
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;
277 }
278 
279 void PersistentMap::add( OString const & key, OString const & value )
280 {
281  auto r = m_entries.emplace(key,value);
282  m_bIsDirty = r.second;
283 }
284 
285 
286 void PersistentMap::put( OString const & key, OString const & value )
287 {
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();
293 }
294 
295 bool PersistentMap::erase( OString const & key )
296 {
297  size_t nCount = m_entries.erase( key);
298  if( !nCount)
299  return false;
300  m_bIsDirty = true;
301  flush();
302  return true;
303 }
304 
305 }
306 
307 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool get(OString *value, OString const &key) const
Definition: dp_persmap.cxx:269
const sal_Char * pChar
static OString encodeString(const OString &rStr)
Definition: dp_persmap.cxx:67
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
static const char PmapMagic[4]
Definition: dp_persmap.cxx:38
char sal_Char
static OString decodeString(const sal_Char *pEncChars, int nLen)
Definition: dp_persmap.cxx:104
int i
void put(OString const &key, OString const &value)
Definition: dp_persmap.cxx:286
unsigned char sal_Bool
PersistentMap()
in mem db
Definition: dp_persmap.cxx:49
bool has(OString const &key) const
Definition: dp_persmap.cxx:264
t_string2string_map m_entries
Definition: dp_persmap.h:38
void add(OString const &key, OString const &value)
Definition: dp_persmap.cxx:279
OUString expandUnoRcUrl(OUString const &url)
Definition: dp_misc.cxx:330
bool erase(OString const &key)
Definition: dp_persmap.cxx:295
::osl::File m_MapFile
Definition: dp_persmap.h:37
#define SAL_WARN(area, stream)