1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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)
44 const char PmapMagic
[4] = {'P','m','p','1'};
46 PersistentMap::PersistentMap( OUString
const & url_
)
47 : m_MapFile( expandUnoRcUrl(url_
) )
49 , m_bToBeCreated( true )
55 PersistentMap::PersistentMap()
56 : m_MapFile( OUString() )
58 , m_bToBeCreated( false )
62 PersistentMap::~PersistentMap()
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();
78 // short circuit for the simple non-encoded case
81 const unsigned char c
= static_cast<unsigned char>(*(pChar
++));
90 // escape chars 0x00..0x0F with "%0".."%F"
91 OStringBuffer
aEncStr( nLen
+ 32);
92 aEncStr
.append( pChar
- (nLen
-i
), nLen
- i
);
95 unsigned char c
= static_cast<unsigned char>(*(pChar
++));
99 c
+= (c
<= 0x09) ? '0' : 'A'-10;
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
;
114 // short circuit for the simple non-encoded case
116 if( *(pChar
++) == '%')
119 return OString( pEncChars
, nLen
);
121 // replace escaped chars with their decoded counterparts
122 OStringBuffer
aDecStr( nLen
);
124 for( i
= nLen
; --i
>= 0;)
127 // handle escaped character
133 if( ('0' <= c
) && (c
<= '9'))
137 OSL_ASSERT( ('A' <= c
) && (c
<= 'F'));
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
;
165 void PersistentMap::readAll()
167 // prepare for re-reading the map-file
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
);
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
))
183 // check header magic
184 for( std::size_t i
= 0; i
< sizeof(PmapMagic
); ++i
)
185 if( aHeaderBytes
[i
] != PmapMagic
[i
])
188 // read key value pairs and add them to the map
189 ByteSequence aKeyLine
;
190 ByteSequence aValLine
;
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
)
197 if( !aKeyLine
.getLength())
199 if( m_MapFile
.readLine( aValLine
) != osl::File::E_None
)
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
)
217 void PersistentMap::flush()
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
;
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
);
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
);
266 // the in-memory map now matches to the file on disk
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())
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
)
295 // HACK: flush now as the extension manager does not seem
296 // to properly destruct this object in some situations
301 bool PersistentMap::erase( OString
const & key
)
303 size_t nCount
= m_entries
.erase( key
);
313 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */