1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_desktop.hxx" 24 25 #include "dp_misc.h" 26 #include "dp_persmap.h" 27 #include "rtl/strbuf.hxx" 28 29 using namespace ::rtl; 30 31 // the persistent map is used to manage a handful of key-value string pairs 32 // this implementation replaces a rather heavy-weight berkeleydb integration 33 34 // the file backing up a persistent map consists of line pairs with 35 // - an encoded key name (with chars 0x00..0x0F being escaped) 36 // - an encoded value name (with chars 0x00..0x0F being escaped) 37 38 namespace dp_misc 39 { 40 41 static const char PmapMagic[4] = {'P','m','p','1'}; 42 43 //______________________________________________________________________________ 44 PersistentMap::PersistentMap( OUString const & url_, bool readOnly ) 45 : m_MapFile( expandUnoRcUrl(url_) ) 46 , m_bReadOnly( readOnly) 47 , m_bIsOpen( false) 48 , m_bToBeCreated( !readOnly) 49 , m_bIsDirty( false) 50 { 51 open(); 52 } 53 54 //______________________________________________________________________________ 55 PersistentMap::PersistentMap() 56 : m_MapFile( OUString()) 57 , m_bReadOnly( false) 58 , m_bIsOpen( false) 59 , m_bToBeCreated( false) 60 , m_bIsDirty( false) 61 {} 62 63 //______________________________________________________________________________ 64 PersistentMap::~PersistentMap() 65 { 66 if( m_bIsDirty) 67 flush(); 68 if( m_bIsOpen) 69 m_MapFile.close(); 70 } 71 72 //______________________________________________________________________________ 73 74 // replace 0x00..0x0F with "%0".."%F" 75 // replace "%" with "%%" 76 static OString encodeString( const OString& rStr) 77 { 78 const sal_Char* pChar = rStr.getStr(); 79 const sal_Int32 nLen = rStr.getLength(); 80 sal_Int32 i = nLen; 81 // short circuit for the simple non-encoded case 82 while( --i >= 0) 83 { 84 const sal_Char c = *(pChar++); 85 if( (0x00 <= c) && (c <= 0x0F)) 86 break; 87 if( c == '%') 88 break; 89 } 90 if( i < 0) 91 return rStr; 92 93 // escape chars 0x00..0x0F with "%0".."%F" 94 OStringBuffer aEncStr( nLen + 32); 95 aEncStr.append( pChar - (nLen-i), nLen - i); 96 while( --i >= 0) 97 { 98 sal_Char c = *(pChar++); 99 if( (0x00 <= c) && (c <= 0x0F)) 100 { 101 aEncStr.append( '%'); 102 c += (c <= 0x09) ? '0' : 'A'-10; 103 } else if( c == '%') 104 aEncStr.append( '%'); 105 aEncStr.append( c); 106 } 107 108 return aEncStr.makeStringAndClear(); 109 } 110 111 //______________________________________________________________________________ 112 113 // replace "%0".."%F" with 0x00..0x0F 114 // replace "%%" with "%" 115 static OString decodeString( const sal_Char* pEncChars, int nLen) 116 { 117 const char* pChar = pEncChars; 118 sal_Int32 i = nLen; 119 // short circuit for the simple non-encoded case 120 while( --i >= 0) 121 if( *(pChar++) == '%') 122 break; 123 if( i < 0) 124 return OString( pEncChars, nLen); 125 126 // replace escaped chars with their decoded counterparts 127 OStringBuffer aDecStr( nLen); 128 pChar = pEncChars; 129 for( i = nLen; --i >= 0;) 130 { 131 sal_Char c = *(pChar++); 132 // handle escaped character 133 if( c == '%') 134 { 135 --i; 136 OSL_ASSERT( i >= 0); 137 c = *(pChar++); 138 if( ('0' <= c) && (c <= '9')) 139 c -= '0'; 140 else 141 { 142 OSL_ASSERT( ('A' <= c) && (c <= 'F')); 143 c -= ('A'-10); 144 } 145 } 146 aDecStr.append( c); 147 } 148 149 return aDecStr.makeStringAndClear(); 150 } 151 152 //______________________________________________________________________________ 153 bool PersistentMap::open() 154 { 155 // open the existing file 156 sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read; 157 if( !m_bReadOnly) 158 nOpenFlags |= osl_File_OpenFlag_Write; 159 160 ::osl::File::RC rcOpen = m_MapFile.open( nOpenFlags); 161 m_bIsOpen = (rcOpen == osl::File::E_None); 162 163 // or create later if needed 164 m_bToBeCreated = (rcOpen == osl::File::E_NOENT) && !m_bIsOpen; 165 if( !m_bIsOpen) 166 return m_bToBeCreated; 167 168 // read header and check magic 169 char aHeaderBytes[ sizeof(PmapMagic)]; 170 sal_uInt64 nBytesRead = 0; 171 m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead); 172 OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes)); 173 if( nBytesRead != sizeof(aHeaderBytes)) 174 return false; 175 // check header magic 176 for( int i = 0; i < (int)sizeof(PmapMagic); ++i) 177 if( aHeaderBytes[i] != PmapMagic[i]) 178 return false; 179 180 // read key value pairs and add them to the map 181 ByteSequence aKeyLine; 182 ByteSequence aValLine; 183 for(;;) 184 { 185 // read key-value line pair 186 // an empty key name indicates the end of the line pairs 187 if( m_MapFile.readLine( aKeyLine) != osl::File::E_None) 188 return false; 189 if( !aKeyLine.getLength()) 190 break; 191 if( m_MapFile.readLine( aValLine) != osl::File::E_None) 192 return false; 193 // decode key and value strings 194 const OString aKeyName = decodeString( (sal_Char*)aKeyLine.getConstArray(), aKeyLine.getLength()); 195 const OString aValName = decodeString( (sal_Char*)aValLine.getConstArray(), aValLine.getLength()); 196 // insert key-value pair into map 197 put( aKeyName, aValName); 198 // check end-of-file status 199 sal_Bool bIsEOF = true; 200 if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None) 201 return false; 202 if( bIsEOF) 203 break; 204 } 205 206 return true; 207 } 208 209 //______________________________________________________________________________ 210 void PersistentMap::flush( void) 211 { 212 if( !m_bIsDirty) 213 return; 214 OSL_ASSERT( !m_bReadOnly); 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 ::osl::File::RC rcOpen = m_MapFile.open( nOpenFlags); 219 m_bIsOpen = (rcOpen == osl::File::E_None); 220 m_bToBeCreated = !m_bIsOpen; 221 } 222 if( !m_bIsOpen) 223 return; 224 225 // write header magic 226 m_MapFile.setPos( osl_Pos_Absolut, 0); 227 sal_uInt64 nBytesWritten = 0; 228 m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten); 229 230 // write key value pairs 231 t_string2string_map::const_iterator it = m_entries.begin(); 232 for(; it != m_entries.end(); ++it) { 233 // write line for key 234 const OString aKeyString = encodeString( (*it).first); 235 const sal_Int32 nKeyLen = aKeyString.getLength(); 236 m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten); 237 OSL_ASSERT( nKeyLen == (sal_Int32)nBytesWritten); 238 m_MapFile.write( "\n", 1, nBytesWritten); 239 // write line for value 240 const OString& rValString = encodeString( (*it).second); 241 const sal_Int32 nValLen = rValString.getLength(); 242 m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten); 243 OSL_ASSERT( nValLen == (sal_Int32)nBytesWritten); 244 m_MapFile.write( "\n", 1, nBytesWritten); 245 } 246 247 // write a file delimiter (an empty key-string) 248 m_MapFile.write( "\n", 1, nBytesWritten); 249 // truncate file here 250 sal_uInt64 nNewFileSize; 251 if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None) 252 m_MapFile.setSize( nNewFileSize); 253 // flush to disk 254 m_MapFile.sync(); 255 // the in-memory map now matches to the file on disk 256 m_bIsDirty = false; 257 } 258 259 //______________________________________________________________________________ 260 bool PersistentMap::has( OString const & key ) const 261 { 262 return get( NULL, key ); 263 } 264 265 //______________________________________________________________________________ 266 bool PersistentMap::get( OString * value, OString const & key ) const 267 { 268 t_string2string_map::const_iterator it = m_entries.find( key); 269 if( it == m_entries.end()) 270 return false; 271 if( value) 272 *value = it->second; 273 return true; 274 } 275 276 //______________________________________________________________________________ 277 void PersistentMap::put( OString const & key, OString const & value ) 278 { 279 if( m_bReadOnly) 280 return; 281 typedef std::pair<t_string2string_map::iterator,bool> InsertRC; 282 InsertRC r = m_entries.insert( t_string2string_map::value_type(key,value)); 283 m_bIsDirty = r.second; 284 (void)r; 285 } 286 287 //______________________________________________________________________________ 288 bool PersistentMap::erase( OString const & key, bool flush_immediately ) 289 { 290 if( m_bReadOnly) 291 return false; 292 size_t nCount = m_entries.erase( key); 293 if( !nCount) 294 return false; 295 m_bIsDirty = true; 296 if( flush_immediately) 297 flush(); 298 return true; 299 } 300 301 //______________________________________________________________________________ 302 t_string2string_map PersistentMap::getEntries() const 303 { 304 // TODO: return by const reference instead? 305 return m_entries; 306 } 307 308 } 309