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 .
21 #include <dp_persmap.h>
22 #include <rtl/byteseq.hxx>
23 #include <rtl/strbuf.hxx>
24 #include <sal/log.hxx>
26 using namespace ::rtl
;
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
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)
38 static const char PmapMagic
[4] = {'P','m','p','1'};
40 PersistentMap::PersistentMap( OUString
const & url_
)
41 : m_MapFile( expandUnoRcUrl(url_
) )
43 , m_bToBeCreated( true )
49 PersistentMap::PersistentMap()
50 : m_MapFile( OUString() )
52 , m_bToBeCreated( false )
56 PersistentMap::~PersistentMap()
65 // replace 0x00..0x0F with "%0".."%F"
66 // replace "%" with "%%"
67 static OString
encodeString( const OString
& rStr
)
69 const sal_Char
* pChar
= rStr
.getStr();
70 const sal_Int32 nLen
= rStr
.getLength();
72 // short circuit for the simple non-encoded case
75 const unsigned char c
= static_cast<unsigned char>(*(pChar
++));
84 // escape chars 0x00..0x0F with "%0".."%F"
85 OStringBuffer
aEncStr( nLen
+ 32);
86 aEncStr
.append( pChar
- (nLen
-i
), nLen
- i
);
89 unsigned char c
= static_cast<unsigned char>(*(pChar
++));
93 c
+= (c
<= 0x09) ? '0' : 'A'-10;
96 aEncStr
.append( char(c
) );
99 return aEncStr
.makeStringAndClear();
102 // replace "%0".."%F" with 0x00..0x0F
103 // replace "%%" with "%"
104 static OString
decodeString( const sal_Char
* pEncChars
, int nLen
)
106 const char* pChar
= pEncChars
;
108 // short circuit for the simple non-encoded case
110 if( *(pChar
++) == '%')
113 return OString( pEncChars
, nLen
);
115 // replace escaped chars with their decoded counterparts
116 OStringBuffer
aDecStr( nLen
);
118 for( i
= nLen
; --i
>= 0;)
120 sal_Char c
= *(pChar
++);
121 // handle escaped character
127 if( ('0' <= c
) && (c
<= '9'))
131 OSL_ASSERT( ('A' <= c
) && (c
<= 'F'));
138 return aDecStr
.makeStringAndClear();
141 void PersistentMap::open()
143 // open the existing file
144 sal_uInt32
const nOpenFlags
= osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
;
146 const osl::File::RC rcOpen
= m_MapFile
.open( nOpenFlags
);
147 m_bIsOpen
= (rcOpen
== osl::File::E_None
);
149 // or create later if needed
150 m_bToBeCreated
&= (rcOpen
== osl::File::E_NOENT
) && !m_bIsOpen
;
159 void PersistentMap::readAll()
161 // prepare for re-reading the map-file
163 const osl::FileBase::RC nRes
= m_MapFile
.setPos( osl_Pos_Absolut
, 0);
164 if (nRes
!= osl::FileBase::E_None
)
166 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes
);
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
))
177 // check header magic
178 for( int i
= 0; i
< int(sizeof(PmapMagic
)); ++i
)
179 if( aHeaderBytes
[i
] != PmapMagic
[i
])
182 // read key value pairs and add them to the map
183 ByteSequence aKeyLine
;
184 ByteSequence aValLine
;
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
)
191 if( !aKeyLine
.getLength())
193 if( m_MapFile
.readLine( aValLine
) != osl::File::E_None
)
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
)
211 void PersistentMap::flush()
215 if( m_bToBeCreated
&& !m_entries
.empty())
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
);
220 m_bToBeCreated
= !m_bIsOpen
;
225 // write header magic
226 const osl::FileBase::RC nRes
= m_MapFile
.setPos( osl_Pos_Absolut
, 0);
227 if (nRes
!= osl::FileBase::E_None
)
229 SAL_WARN("desktop.deployment", "setPos failed with " << +nRes
);
232 sal_uInt64 nBytesWritten
= 0;
233 m_MapFile
.write( PmapMagic
, sizeof(PmapMagic
), nBytesWritten
);
235 // write key value pairs
236 for (auto const& entry
: m_entries
)
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
);
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
);
260 // the in-memory map now matches to the file on disk
264 bool PersistentMap::has( OString
const & key
) const
266 return get( nullptr, key
);
269 bool PersistentMap::get( OString
* value
, OString
const & key
) const
271 t_string2string_map::const_iterator it
= m_entries
.find( key
);
272 if( it
== m_entries
.end())
279 void PersistentMap::add( OString
const & key
, OString
const & value
)
281 auto r
= m_entries
.emplace(key
,value
);
282 m_bIsDirty
= r
.second
;
286 void PersistentMap::put( OString
const & key
, OString
const & value
)
289 // HACK: flush now as the extension manager does not seem
290 // to properly destruct this object in some situations
295 bool PersistentMap::erase( OString
const & key
)
297 size_t nCount
= m_entries
.erase( key
);
307 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */