Lines 19-248
Link Here
|
19 |
* |
19 |
* |
20 |
*************************************************************/ |
20 |
*************************************************************/ |
21 |
|
21 |
|
22 |
|
|
|
23 |
|
24 |
// MARKER(update_precomp.py): autogen include statement, do not remove |
22 |
// MARKER(update_precomp.py): autogen include statement, do not remove |
25 |
#include "precompiled_desktop.hxx" |
23 |
#include "precompiled_desktop.hxx" |
26 |
|
24 |
|
27 |
#include "dp_misc.h" |
25 |
#include "dp_misc.h" |
28 |
#include "dp_ucb.h" |
|
|
29 |
#include "dp_persmap.h" |
26 |
#include "dp_persmap.h" |
30 |
#include "rtl/strbuf.hxx" |
27 |
#include "rtl/strbuf.hxx" |
31 |
#include "rtl/ustrbuf.hxx" |
|
|
32 |
#include "osl/file.hxx" |
33 |
#include "osl/thread.h" |
34 |
|
35 |
|
28 |
|
36 |
using namespace ::com::sun::star; |
|
|
37 |
using namespace ::com::sun::star::uno; |
38 |
using namespace ::rtl; |
29 |
using namespace ::rtl; |
39 |
using ::osl::File; |
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) |
40 |
|
37 |
|
41 |
namespace dp_misc |
38 |
namespace dp_misc |
42 |
{ |
39 |
{ |
43 |
|
40 |
|
|
|
41 |
static const char PmapMagic[4] = {'P','m','p','1'}; |
42 |
|
44 |
//______________________________________________________________________________ |
43 |
//______________________________________________________________________________ |
45 |
void PersistentMap::throw_rtexc( int err, char const * pmsg ) const |
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) |
46 |
{ |
50 |
{ |
47 |
OUStringBuffer buf; |
51 |
open(); |
48 |
buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("[") ); |
|
|
49 |
buf.append( m_sysPath ); |
50 |
buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("] Berkeley Db error (") ); |
51 |
buf.append( static_cast<sal_Int32>(err) ); |
52 |
buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("): ") ); |
53 |
if (pmsg == 0) |
54 |
pmsg = DbEnv::strerror(err); |
55 |
const OString msg(pmsg); |
56 |
buf.append( OUString( msg.getStr(), msg.getLength(), |
57 |
osl_getThreadTextEncoding() ) ); |
58 |
const OUString msg_(buf.makeStringAndClear()); |
59 |
OSL_ENSURE( 0, rtl::OUStringToOString( |
60 |
msg_, RTL_TEXTENCODING_UTF8 ).getStr() ); |
61 |
throw RuntimeException( msg_, Reference<XInterface>() ); |
62 |
} |
52 |
} |
63 |
|
53 |
|
64 |
//______________________________________________________________________________ |
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 |
//______________________________________________________________________________ |
65 |
PersistentMap::~PersistentMap() |
64 |
PersistentMap::~PersistentMap() |
66 |
{ |
65 |
{ |
67 |
try { |
66 |
if( m_bIsDirty) |
68 |
m_db.close(0); |
67 |
flush(); |
69 |
} |
68 |
if( m_bIsOpen) |
70 |
catch (DbException & exc) { |
69 |
m_MapFile.close(); |
71 |
(void) exc; // avoid warnings |
|
|
72 |
OSL_ENSURE( 0, DbEnv::strerror( exc.get_errno() ) ); |
73 |
} |
74 |
} |
70 |
} |
75 |
|
71 |
|
76 |
//______________________________________________________________________________ |
72 |
//______________________________________________________________________________ |
77 |
PersistentMap::PersistentMap( OUString const & url_, bool readOnly ) |
73 |
|
78 |
: m_db( 0, 0 ) |
74 |
// replace 0x00..0x0F with "%0".."%F" |
|
|
75 |
// replace "%" with "%%" |
76 |
static OString encodeString( const OString& rStr) |
79 |
{ |
77 |
{ |
80 |
try { |
78 |
const sal_Char* pStr = rStr.getStr(); |
81 |
OUString url( expandUnoRcUrl(url_) ); |
79 |
const sal_Int32 nLen = rStr.getLength(); |
82 |
if ( File::getSystemPathFromFileURL( url, m_sysPath ) != File::E_None ) |
80 |
sal_Int32 i = nLen; |
83 |
{ |
81 |
for(; --i >= 0; ++pStr) |
84 |
OSL_ASSERT( false ); |
82 |
if( (*pStr == '\0') || (*pStr == '%')) |
85 |
} |
83 |
break; |
86 |
OString cstr_sysPath( |
84 |
if( i < 0) |
87 |
OUStringToOString( m_sysPath, RTL_TEXTENCODING_UTF8 ) ); |
85 |
return rStr; |
88 |
char const * pcstr_sysPath = cstr_sysPath.getStr(); |
86 |
|
89 |
|
87 |
OStringBuffer aEncStr( nLen + 32); |
90 |
u_int32_t flags = DB_CREATE; |
88 |
pStr -= nLen - i; |
91 |
if (readOnly) { |
89 |
for( i = nLen; --i >= 0; ++pStr) |
92 |
flags = DB_RDONLY; |
90 |
{ |
93 |
if (! create_ucb_content( |
91 |
sal_Char c = *pStr; |
94 |
0, url, |
92 |
if( (0x00 <= c) && (c <= 0x0F)) |
95 |
Reference<com::sun::star::ucb::XCommandEnvironment>(), |
93 |
{ |
96 |
false /* no throw */ )) { |
94 |
aEncStr.append( '%'); |
97 |
// ignore non-existent file in read-only mode: simulate empty db |
95 |
c += (c <= 9) ? '0' : 'A'-10; |
98 |
pcstr_sysPath = 0; |
96 |
} else if( c == '%') |
99 |
flags = DB_CREATE; |
97 |
aEncStr.append( '%'); |
100 |
} |
98 |
aEncStr.append( c); |
101 |
} |
99 |
} |
102 |
|
100 |
|
103 |
int err = m_db.open( |
101 |
return aEncStr.makeStringAndClear(); |
104 |
// xxx todo: DB_THREAD, DB_DBT_MALLOC currently not used |
|
|
105 |
0, pcstr_sysPath, 0, DB_HASH, flags/* | DB_THREAD*/, 0664 /* fs mode */ ); |
106 |
if (err != 0) |
107 |
throw_rtexc(err); |
108 |
} |
109 |
catch (DbException & exc) { |
110 |
throw_rtexc( exc.get_errno(), exc.what() ); |
111 |
} |
112 |
} |
102 |
} |
113 |
|
103 |
|
114 |
//______________________________________________________________________________ |
104 |
//______________________________________________________________________________ |
115 |
PersistentMap::PersistentMap() |
105 |
|
116 |
: m_db( 0, 0 ) |
106 |
// replace "%0".."%F" with 0x00..0x0F |
|
|
107 |
// replace "%%" with "%" |
108 |
static OString decodeString( const sal_Char* pEncChars, int nLen) |
109 |
{ |
110 |
sal_Int32 i = nLen; |
111 |
const char* pChar = pEncChars; |
112 |
for(; --i >= 0; ++pChar) |
113 |
if( *pChar == '%') |
114 |
break; |
115 |
if( i < 0) |
116 |
return OString( pEncChars, nLen); |
117 |
|
118 |
OStringBuffer aDecStr( nLen); |
119 |
pChar = pEncChars; |
120 |
for( i = nLen; --i >= 0; ++pChar) |
121 |
{ |
122 |
sal_Char c = pChar[0]; |
123 |
if( c == '%') |
124 |
{ |
125 |
c = pChar[1]; |
126 |
--i; |
127 |
if( ('0' <= c) && (c <= '9')) |
128 |
c -= '0'; |
129 |
else if( ('A' <= c) && (c <= 'F')) |
130 |
c -= ('A'-10); |
131 |
} |
132 |
aDecStr.append( c); |
133 |
} |
134 |
|
135 |
return aDecStr.makeStringAndClear(); |
136 |
} |
137 |
|
138 |
//______________________________________________________________________________ |
139 |
bool PersistentMap::open() |
117 |
{ |
140 |
{ |
118 |
try { |
141 |
// open the existing file |
119 |
// xxx todo: DB_THREAD, DB_DBT_MALLOC currently not used |
142 |
sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read; |
120 |
int err = m_db.open( 0, 0, 0, DB_HASH, DB_CREATE/* | DB_THREAD*/, 0 ); |
143 |
if( !m_bReadOnly) |
121 |
if (err != 0) |
144 |
nOpenFlags |= osl_File_OpenFlag_Write; |
122 |
throw_rtexc(err); |
145 |
|
123 |
} |
146 |
::osl::File::RC rcOpen = m_MapFile.open( nOpenFlags); |
124 |
catch (DbException & exc) { |
147 |
m_bIsOpen = (rcOpen == osl::File::E_None); |
125 |
throw_rtexc( exc.get_errno(), exc.what() ); |
148 |
|
126 |
} |
149 |
// or create later if needed |
|
|
150 |
if( !m_bIsOpen) |
151 |
{ |
152 |
m_bToBeCreated &= (rcOpen == osl::File::E_NOENT); |
153 |
return m_bToBeCreated; |
154 |
} |
155 |
|
156 |
// read header and check magic |
157 |
char aHeaderBytes[ sizeof(PmapMagic)]; |
158 |
sal_uInt64 nBytesRead = 0; |
159 |
m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead); |
160 |
OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes)); |
161 |
if( nBytesRead != sizeof(aHeaderBytes)) |
162 |
return false; |
163 |
// TODO: check magic |
164 |
|
165 |
// read key value pairs |
166 |
ByteSequence aKeyLine; |
167 |
ByteSequence aValLine; |
168 |
for(;;) |
169 |
{ |
170 |
// read key-value line pair |
171 |
::osl::File::RC rcKey = m_MapFile.readLine( aKeyLine); |
172 |
if( rcKey != osl::File::E_None) |
173 |
return false; |
174 |
::osl::File::RC rcVal = m_MapFile.readLine( aValLine); |
175 |
if( rcVal != osl::File::E_None) |
176 |
return false; |
177 |
// decode key and value strings |
178 |
const OString aKeyName = decodeString( (sal_Char*)aKeyLine.getConstArray(), aKeyLine.getLength()); |
179 |
const OString aValName = decodeString( (sal_Char*)aValLine.getConstArray(), aValLine.getLength()); |
180 |
// insert key-value pair into map |
181 |
put( aKeyName, aValName); |
182 |
// check end-of-file status |
183 |
sal_Bool bIsEOF = true; |
184 |
m_MapFile.isEndOfFile( &bIsEOF); |
185 |
if( bIsEOF) |
186 |
break; |
187 |
} |
188 |
|
189 |
return true; |
190 |
} |
191 |
|
192 |
//______________________________________________________________________________ |
193 |
void PersistentMap::flush( void) |
194 |
{ |
195 |
if( !m_bIsDirty) |
196 |
return; |
197 |
OSL_ASSERT( !m_bReadOnly); |
198 |
if( m_bToBeCreated && !m_entries.empty()) |
199 |
{ |
200 |
const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create; |
201 |
::osl::File::RC rcOpen = m_MapFile.open( nOpenFlags); |
202 |
m_bIsOpen = (rcOpen == osl::File::E_None); |
203 |
m_bToBeCreated = !m_bIsOpen; |
204 |
} |
205 |
if( !m_bIsOpen) |
206 |
return; |
207 |
|
208 |
// write header magic |
209 |
m_MapFile.setPos( osl_Pos_Absolut, 0); |
210 |
m_MapFile.setSize( 0); |
211 |
sal_uInt64 nBytesWritten = 0; |
212 |
m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten); |
213 |
|
214 |
// write key value pairs |
215 |
t_string2string_map::const_iterator it = m_entries.begin(); |
216 |
for(; it != m_entries.end(); ++it) { |
217 |
// write line for key |
218 |
const OString aKeyString = encodeString( (*it).first); |
219 |
const sal_Int32 nKeyLen = aKeyString.getLength(); |
220 |
m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten); |
221 |
OSL_ASSERT( nKeyLen == (sal_Int32)nBytesWritten); |
222 |
m_MapFile.write( "\n", 1, nBytesWritten); |
223 |
// write line for value |
224 |
const OString& rValString = encodeString( (*it).second); |
225 |
const sal_Int32 nValLen = rValString.getLength(); |
226 |
m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten); |
227 |
OSL_ASSERT( nValLen == (sal_Int32)nBytesWritten); |
228 |
m_MapFile.write( "\n", 1, nBytesWritten); |
229 |
} |
230 |
|
231 |
m_MapFile.sync(); |
232 |
m_bIsDirty = false; |
127 |
} |
233 |
} |
128 |
|
234 |
|
129 |
//______________________________________________________________________________ |
235 |
//______________________________________________________________________________ |
130 |
bool PersistentMap::has( OString const & key ) const |
236 |
bool PersistentMap::has( OString const & key ) const |
131 |
{ |
237 |
{ |
132 |
return get( 0, key ); |
238 |
return get( NULL, key ); |
133 |
} |
239 |
} |
134 |
|
240 |
|
135 |
//______________________________________________________________________________ |
241 |
//______________________________________________________________________________ |
136 |
bool PersistentMap::get( OString * value, OString const & key ) const |
242 |
bool PersistentMap::get( OString * value, OString const & key ) const |
137 |
{ |
243 |
{ |
138 |
try { |
244 |
t_string2string_map::const_iterator it = m_entries.find( key); |
139 |
Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() ); |
245 |
if( it == m_entries.end()) |
140 |
Dbt dbData; |
246 |
return false; |
141 |
int err = m_db.get( 0, &dbKey, &dbData, 0 ); |
247 |
*value = it->second; |
142 |
if (err == DB_NOTFOUND) |
248 |
return true; |
143 |
return false; |
|
|
144 |
if (err == 0) { |
145 |
if (value != 0) { |
146 |
*value = OString( |
147 |
static_cast< sal_Char const * >(dbData.get_data()), |
148 |
dbData.get_size() ); |
149 |
} |
150 |
return true; |
151 |
} |
152 |
throw_rtexc(err); |
153 |
} |
154 |
catch (DbException & exc) { |
155 |
throw_rtexc( exc.get_errno(), exc.what() ); |
156 |
} |
157 |
return false; // avoiding warning |
158 |
} |
249 |
} |
159 |
|
250 |
|
160 |
//______________________________________________________________________________ |
251 |
//______________________________________________________________________________ |
161 |
void PersistentMap::put( OString const & key, OString const & value ) |
252 |
void PersistentMap::put( OString const & key, OString const & value ) |
162 |
{ |
253 |
{ |
163 |
try { |
254 |
if( m_bReadOnly) |
164 |
Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() ); |
255 |
return; |
165 |
Dbt dbData( const_cast< sal_Char * >( |
256 |
typedef std::pair<t_string2string_map::iterator,bool> InsertRC; |
166 |
value.getStr()), value.getLength() ); |
257 |
InsertRC r = m_entries.insert( t_string2string_map::value_type(key,value)); |
167 |
int err = m_db.put( 0, &dbKey, &dbData, 0 ); |
258 |
m_bIsDirty = r.second; |
168 |
if (err == 0) { |
259 |
(void)r; |
169 |
#if OSL_DEBUG_LEVEL > 0 |
|
|
170 |
OString v; |
171 |
OSL_ASSERT( get( &v, key ) ); |
172 |
OSL_ASSERT( v.equals( value ) ); |
173 |
#endif |
174 |
err = m_db.sync(0); |
175 |
} |
176 |
if (err != 0) |
177 |
throw_rtexc(err); |
178 |
} |
179 |
catch (DbException & exc) { |
180 |
throw_rtexc( exc.get_errno(), exc.what() ); |
181 |
} |
182 |
} |
260 |
} |
183 |
|
261 |
|
184 |
//______________________________________________________________________________ |
262 |
//______________________________________________________________________________ |
185 |
bool PersistentMap::erase( OString const & key, bool flush_immediately ) |
263 |
bool PersistentMap::erase( OString const & key, bool flush_immediately ) |
186 |
{ |
264 |
{ |
187 |
try { |
265 |
if( m_bReadOnly) |
188 |
Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() ); |
266 |
return false; |
189 |
int err = m_db.del( &dbKey, 0 ); |
267 |
m_bIsDirty = true; |
190 |
if (err == 0) { |
268 |
size_t nCount = m_entries.erase( key); |
191 |
if (flush_immediately) { |
269 |
if( !nCount) |
192 |
err = m_db.sync(0); |
270 |
return false; |
193 |
if (err != 0) |
271 |
if( flush_immediately) |
194 |
throw_rtexc(err); |
272 |
flush(); |
195 |
} |
273 |
return true; |
196 |
return true; |
|
|
197 |
} |
198 |
if (err == DB_NOTFOUND) |
199 |
return false; |
200 |
throw_rtexc(err); |
201 |
} |
202 |
catch (DbException & exc) { |
203 |
throw_rtexc( exc.get_errno(), exc.what() ); |
204 |
} |
205 |
return false; // avoiding warning |
206 |
} |
274 |
} |
207 |
|
275 |
|
208 |
//______________________________________________________________________________ |
276 |
//______________________________________________________________________________ |
209 |
t_string2string_map PersistentMap::getEntries() const |
277 |
t_string2string_map PersistentMap::getEntries() const |
210 |
{ |
278 |
{ |
211 |
try { |
279 |
// TODO: return by reference instead? |
212 |
Dbc * pcurs = 0; |
280 |
return m_entries; |
213 |
int err = m_db.cursor( 0, &pcurs, 0 ); |
|
|
214 |
if (err != 0) |
215 |
throw_rtexc(err); |
216 |
|
217 |
t_string2string_map ret; |
218 |
for (;;) { |
219 |
Dbt dbKey, dbData; |
220 |
err = pcurs->get( &dbKey, &dbData, DB_NEXT ); |
221 |
if (err == DB_NOTFOUND) |
222 |
break; |
223 |
if (err != 0) |
224 |
throw_rtexc(err); |
225 |
|
226 |
::std::pair<t_string2string_map::iterator, bool > insertion( |
227 |
ret.insert( t_string2string_map::value_type( |
228 |
t_string2string_map::value_type( |
229 |
OString( static_cast< sal_Char const * >( |
230 |
dbKey.get_data()), |
231 |
dbKey.get_size() ), |
232 |
OString( static_cast< sal_Char const * >( |
233 |
dbData.get_data()), |
234 |
dbData.get_size() ) ) ) ) ); |
235 |
OSL_ASSERT( insertion.second ); |
236 |
} |
237 |
err = pcurs->close(); |
238 |
if (err != 0) |
239 |
throw_rtexc(err); |
240 |
return ret; |
241 |
} |
242 |
catch (DbException & exc) { |
243 |
throw_rtexc( exc.get_errno(), exc.what() ); |
244 |
} |
245 |
return t_string2string_map(); // avoiding warning |
246 |
} |
281 |
} |
247 |
|
282 |
|
248 |
} |
283 |
} |