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 <o3tl/safeint.hxx>
27 #include <rtl/byteseq.hxx>
28 #include <rtl/strbuf.hxx>
29 #include <sal/log.hxx>
31 using namespace ::rtl
;
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
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)
43 const char PmapMagic
[4] = {'P','m','p','1'};
45 PersistentMap::PersistentMap( OUString
const & url_
)
46 : m_MapFile( expandUnoRcUrl(url_
) )
48 , m_bToBeCreated( true )
54 PersistentMap::PersistentMap()
55 : m_MapFile( OUString() )
57 , m_bToBeCreated( false )
61 PersistentMap::~PersistentMap()
70 // replace 0x00..0x0F with "%0".."%F"
71 // replace "%" with "%%"
72 static OString
encodeString( const OString
& rStr
)
74 const char* pChar
= rStr
.getStr();
75 const sal_Int32 nLen
= rStr
.getLength();
77 // short circuit for the simple non-encoded case
80 const unsigned char c
= static_cast<unsigned char>(*(pChar
++));
89 // escape chars 0x00..0x0F with "%0".."%F"
90 OStringBuffer
aEncStr( nLen
+ 32);
91 aEncStr
.append( pChar
- (nLen
-i
), nLen
- i
);
94 unsigned char c
= static_cast<unsigned char>(*(pChar
++));
98 c
+= (c
<= 0x09) ? '0' : 'A'-10;
100 aEncStr
.append( '%');
101 aEncStr
.append( char(c
) );
104 return aEncStr
.makeStringAndClear();
107 // replace "%0".."%F" with 0x00..0x0F
108 // replace "%%" with "%"
109 static OString
decodeString( const char* pEncChars
, int nLen
)
111 const char* pChar
= pEncChars
;
113 // short circuit for the simple non-encoded case
115 if( *(pChar
++) == '%')
118 return OString( pEncChars
, nLen
);
120 // replace escaped chars with their decoded counterparts
121 OStringBuffer
aDecStr( nLen
);
123 for( i
= nLen
; --i
>= 0;)
126 // handle escaped character
132 if( ('0' <= c
) && (c
<= '9'))
136 OSL_ASSERT( ('A' <= c
) && (c
<= 'F'));
143 return aDecStr
.makeStringAndClear();
146 void PersistentMap::open()
148 // open the existing file
149 sal_uInt32
const nOpenFlags
= osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
;
151 const osl::File::RC rcOpen
= m_MapFile
.open( nOpenFlags
);
152 m_bIsOpen
= (rcOpen
== osl::File::E_None
);
154 // or create later if needed
155 m_bToBeCreated
&= (rcOpen
== osl::File::E_NOENT
) && !m_bIsOpen
;
164 void PersistentMap::readAll()
166 // prepare for re-reading the map-file
168 const osl::FileBase::RC nRes
= m_MapFile
.setPos( osl_Pos_Absolut
, 0);
169 if (nRes
!= osl::FileBase::E_None
)
171 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes
);
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
))
182 // check header magic
183 for( std::size_t i
= 0; i
< sizeof(PmapMagic
); ++i
)
184 if( aHeaderBytes
[i
] != PmapMagic
[i
])
187 // read key value pairs and add them to the map
188 ByteSequence aKeyLine
;
189 ByteSequence aValLine
;
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
)
196 if( !aKeyLine
.getLength())
198 if( m_MapFile
.readLine( aValLine
) != osl::File::E_None
)
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
)
216 void PersistentMap::flush()
220 if( m_bToBeCreated
&& !m_entries
.empty())
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
);
225 m_bToBeCreated
= !m_bIsOpen
;
230 // write header magic
231 const osl::FileBase::RC nRes
= m_MapFile
.setPos( osl_Pos_Absolut
, 0);
232 if (nRes
!= osl::FileBase::E_None
)
234 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes
);
237 sal_uInt64 nBytesWritten
= 0;
238 m_MapFile
.write( PmapMagic
, sizeof(PmapMagic
), nBytesWritten
);
240 // write key value pairs
241 for (auto const& entry
: m_entries
)
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
);
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
);
265 // the in-memory map now matches to the file on disk
269 bool PersistentMap::has( OString
const & key
) const
271 return get( nullptr, key
);
274 bool PersistentMap::get( OString
* value
, OString
const & key
) const
276 t_string2string_map::const_iterator it
= m_entries
.find( key
);
277 if( it
== m_entries
.end())
284 void PersistentMap::add( OString
const & key
, OString
const & value
)
286 auto r
= m_entries
.emplace(key
,value
);
287 m_bIsDirty
= r
.second
;
291 void PersistentMap::put( OString
const & key
, OString
const & value
)
294 // HACK: flush now as the extension manager does not seem
295 // to properly destruct this object in some situations
300 bool PersistentMap::erase( OString
const & key
)
302 size_t nCount
= m_entries
.erase( key
);
312 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */