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 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_xmlhelp.hxx"
26 
27 #include "db.hxx"
28 
29 #include <rtl/alloc.h>
30 #include <cstring>
31 
32 #include "com/sun/star/io/XSeekable.hpp"
33 
34 #include "osl/file.hxx"
35 #include "osl/thread.hxx"
36 #ifdef TEST_DBHELP
37 #include <osl/time.h>
38 #endif
39 
40 using namespace com::sun::star;
41 using namespace com::sun::star::uno;
42 using namespace com::sun::star::io;
43 
44 namespace berkeleydbproxy {
45 
46 //----------------------------------------------------------------------------
47 namespace db_internal
48 {
49     // static void raise_error(int dberr, const char * where);
50 
51     static inline int check_error(int dberr, const char * where)
52     {
53 		(void)where;
54 
55         // if (dberr) raise_error(dberr,where);
56         return dberr;
57     }
58 }
59 
60 void DBData::copyToBuffer( const char* pSrcData, int nSize )
61 {
62 	m_nSize = nSize;
63 	delete [] m_pBuffer;
64 	m_pBuffer = new char[m_nSize+1];
65 	memcpy( m_pBuffer, pSrcData, m_nSize );
66 	m_pBuffer[m_nSize] = 0;
67 }
68 
69 
70 // DBHelp
71 
72 bool DBHelp::implReadLenAndData( const char* pData, int& riPos, DBData& rValue )
73 {
74 	bool bSuccess = false;
75 
76 	// Read key len
77 	const char* pStartPtr = pData + riPos;
78 	char* pEndPtr;
79 	sal_Int32 nKeyLen = strtol( pStartPtr, &pEndPtr, 16 );
80 	if( pEndPtr == pStartPtr )
81 		return bSuccess;
82 	riPos += (pEndPtr - pStartPtr) + 1;
83 
84 	const char* pKeySrc = pData + riPos;
85 	rValue.copyToBuffer( pKeySrc, nKeyLen );
86 	riPos += nKeyLen + 1;
87 
88 	bSuccess = true;
89 	return bSuccess;
90 }
91 
92 #ifdef TEST_DBHELP
93 
94 typedef std::pair< rtl::OString, rtl::OString >		KeyValPair;
95 typedef std::vector< KeyValPair >					KeyValPairVector;
96 
97 void testWriteKeyValue( FILE* pFile, const KeyValPair& rKeyValPair )
98 {
99 	if( pFile == NULL )
100 		return;
101 	char cLF = 10;
102 
103 	const rtl::OString& aKeyStr = rKeyValPair.first;
104 	const rtl::OString& aValueStr = rKeyValPair.second;
105 	int nKeyLen = aKeyStr.getLength();
106 	int nValueLen = aValueStr.getLength();
107 	fprintf( pFile, "%x ", nKeyLen );
108 	if( nKeyLen > 0 )
109 		fwrite( aKeyStr.getStr(), 1, nKeyLen, pFile );
110 	fprintf( pFile, " %x ", nValueLen );
111 	if( nValueLen > 0 )
112 		fwrite( aValueStr.getStr(), 1, nValueLen, pFile );
113 	fprintf( pFile, "%c", cLF );
114 }
115 
116 bool DBHelp::testAgainstDb( const rtl::OUString& fileURL, bool bOldDbAccess )
117 {
118 	bool bSuccess = true;
119 
120 	KeyValPairVector avKeyValPair;
121 
122 	rtl::OUString aOutFileName = fileURL;
123 	aOutFileName += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("_TestOut"));
124 	if( bOldDbAccess )
125 		aOutFileName += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("_Old"));
126 #ifdef WNT
127     FILE* pFile = _wfopen( aOutFileName.getStr(), L"wb" );
128 #else
129     rtl::OString sFile = rtl::OUStringToOString(aOutFileName, osl_getThreadTextEncoding());
130 	FILE* pFile = fopen( sFile.getStr(), "wb" );
131 #endif
132 	// Get all values
133 	Db table;
134 	if( 0 == table.open( 0,fileURL,DB_BTREE,DB_RDONLY,0644 ) )
135 	{
136 		bool first = true;
137 
138 		Dbc* cursor = 0;
139 		table.cursor( 0,&cursor,0 );
140 		Dbt key_,data;
141 		key_.set_flags( DB_DBT_MALLOC ); // Initially the cursor must allocate the necessary memory
142 		data.set_flags( DB_DBT_MALLOC );
143 
144 		while( cursor && DB_NOTFOUND != cursor->get( &key_,&data,DB_NEXT ) )
145 		{
146 			rtl::OString keyword( static_cast<sal_Char*>(key_.get_data()),
147 								  key_.get_size() );
148 			rtl::OString value( static_cast<sal_Char*>(data.get_data()),
149 								data.get_size() );
150 
151 			KeyValPair aPair( keyword, value );
152 			avKeyValPair.push_back( aPair );
153 			if( pFile != NULL )
154 				testWriteKeyValue( pFile, aPair );
155 
156 			if( first )
157 			{
158 				key_.set_flags( DB_DBT_REALLOC );
159 				data.set_flags( DB_DBT_REALLOC );
160 				first = false;
161 			}
162 		}
163 
164 		if( cursor ) cursor->close();
165 	}
166 	table.close( 0 );
167 
168 	// TEST
169 	DBData aDBData;
170 	Db tableTest;
171 	Dbt data;
172 
173 	int nOkCount = 0;
174 	int nErrCount = 0;
175 
176 	bool bTestSuccess;
177 	const char* pTestReadData = NULL;
178 	int nTestReadDataSize = 0;
179 
180 	sal_uInt32 starttime = osl_getGlobalTimer();
181 	sal_uInt32 afterfirsttime = starttime;
182 
183 	if( pFile != NULL )
184 	{
185 		if( bOldDbAccess )
186 			fprintf( pFile, "\nTesting old access:\n" );
187 		else
188 			fprintf( pFile, "\nTesting new access:\n" );
189 	}
190 
191 	KeyValPairVector::const_iterator it;
192 	bool bFirst = true;
193 	for( it = avKeyValPair.begin() ; it != avKeyValPair.end() ; ++it )
194 	{
195 		const KeyValPair& rKeyValPair = *it;
196 
197 		const rtl::OString& aKeyStr = rKeyValPair.first;
198 		const rtl::OString& aValueStr = rKeyValPair.second;
199 		int nKeyLen = aKeyStr.getLength();
200 		int nValueLen = aValueStr.getLength();
201 
202 		const sal_Char* ptr = aValueStr.getStr();
203 
204 		bTestSuccess = false;
205 		pTestReadData = NULL;
206 		nTestReadDataSize = 0;
207 		if( bOldDbAccess )
208 		{
209 			if( bFirst )
210 			{
211 				if( tableTest.open( 0,fileURL, DB_BTREE,DB_RDONLY,0644 ) )
212 				{
213 					if( pFile != NULL )
214 						fprintf( pFile, "Cannot open database\n" );
215 
216 					break;
217 				}
218 			}
219 
220 			Dbt key( static_cast< void* >( const_cast< sal_Char* >( aKeyStr.getStr() ) ), aKeyStr.getLength() );
221 			int err = tableTest.get( 0, &key, &data, 0 );
222 			if( err == 0 )
223 			{
224 				bTestSuccess = true;
225 				pTestReadData = static_cast< sal_Char* >( data.get_data() );
226 				nTestReadDataSize = data.get_size();
227 			}
228 		}
229 		else
230 		{
231 			bTestSuccess = getValueForKey( aKeyStr, aDBData );
232 			if( bTestSuccess )
233 			{
234 				pTestReadData = aDBData.getData();
235 				nTestReadDataSize = aDBData.getSize();
236 			}
237 		}
238 		if( bFirst )
239 		{
240 			afterfirsttime = osl_getGlobalTimer();
241 			bFirst = false;
242 		}
243 		int nError = 0;
244 		if( bTestSuccess && pTestReadData != NULL )
245 		{
246 			int nCmp = memcmp( ptr, pTestReadData, nValueLen );
247 			if( nCmp == 0 )
248 				++nOkCount;
249 			else
250 				nError = 1;
251 
252 			if( nValueLen != nTestReadDataSize )
253 				nError = 2;
254 		}
255 		else
256 			nError = 3;
257 
258 		if( nError != 0 )
259 		{
260 			bSuccess = false;
261 			++nErrCount;
262 
263 			if( pFile != NULL )
264 			{
265 				fprintf( pFile, "ERROR, not found:\n" );
266 				testWriteKeyValue( pFile, rKeyValPair );
267 				fprintf( pFile, "\nError Code: %d\n", nError );
268 			}
269 		}
270 	}
271 	tableTest.close( 0 );
272 
273 	sal_uInt32 endtime = osl_getGlobalTimer();
274 	double dDiffTime = (endtime-starttime) / 1000.0;
275 	double dDiffFirstTime = (afterfirsttime-starttime) / 1000.0;
276 	if( pFile != NULL )
277 	{
278 		int nCount = avKeyValPair.size();
279 		fprintf( pFile, "%d key/values in total, read %d correctly, %d errors\n",
280 			nCount, nOkCount, nErrCount );
281 		fprintf( pFile, "Time taken: %g s (First access %g s)\n", dDiffTime, dDiffFirstTime );
282 		fprintf( pFile, "Average time per access: %g s\n", dDiffTime / nCount );
283 	}
284 
285 	if( pFile != NULL )
286 		fclose( pFile );
287 
288 	return bSuccess;
289 }
290 
291 #endif
292 
293 
294 void DBHelp::createHashMap( bool bOptimizeForPerformance )
295 {
296 	releaseHashMap();
297 	if( bOptimizeForPerformance )
298 	{
299 		if( m_pStringToDataMap != NULL )
300 			return;
301 		m_pStringToDataMap = new StringToDataMap();
302 	}
303 	else
304 	{
305 		if( m_pStringToValPosMap != NULL )
306 			return;
307 		m_pStringToValPosMap = new StringToValPosMap();
308 	}
309 
310 	Reference< XInputStream > xIn = m_xSFA->openFileRead( m_aFileURL );
311 	if( xIn.is() )
312 	{
313 		Sequence< sal_Int8 > aData;
314 		sal_Int32 nSize = m_xSFA->getSize( m_aFileURL );
315 		sal_Int32 nRead = xIn->readBytes( aData, nSize );
316 
317 		const char* pData = (const char*)aData.getConstArray();
318 		int iPos = 0;
319 		while( iPos < nRead )
320 		{
321 			DBData aDBKey;
322 			if( !implReadLenAndData( pData, iPos, aDBKey ) )
323 				break;
324 
325 			rtl::OString aOKeyStr = aDBKey.getData();
326 
327 			// Read val len
328 			const char* pStartPtr = pData + iPos;
329 			char* pEndPtr;
330 			sal_Int32 nValLen = strtol( pStartPtr, &pEndPtr, 16 );
331 			if( pEndPtr == pStartPtr )
332 				break;
333 
334 			iPos += (pEndPtr - pStartPtr) + 1;
335 
336 			if( bOptimizeForPerformance )
337 			{
338 				const char* pValSrc = pData + iPos;
339 				rtl::OString aValStr( pValSrc, nValLen );
340 				(*m_pStringToDataMap)[aOKeyStr] = aValStr;
341 			}
342 			else
343 			{
344 				// store value start position
345 				(*m_pStringToValPosMap)[aOKeyStr] = std::pair<int,int>( iPos, nValLen );
346 			}
347 			iPos += nValLen + 1;
348 		}
349 
350 		xIn->closeInput();
351 	}
352 }
353 
354 void DBHelp::releaseHashMap( void )
355 {
356 	if( m_pStringToDataMap != NULL )
357 	{
358 		delete m_pStringToDataMap;
359 		m_pStringToDataMap = NULL;
360 	}
361 	if( m_pStringToValPosMap != NULL )
362 	{
363 		delete m_pStringToValPosMap;
364 		m_pStringToValPosMap = NULL;
365 	}
366 }
367 
368 
369 bool DBHelp::getValueForKey( const rtl::OString& rKey, DBData& rValue )
370 {
371 	bool bSuccess = false;
372 	if( !m_xSFA.is() )
373 		return bSuccess;
374 
375 	try
376 	{
377 
378 	if( m_pStringToDataMap == NULL && m_pStringToValPosMap == NULL )
379 	{
380 		bool bOptimizeForPerformance = false;
381 		createHashMap( bOptimizeForPerformance );
382 	}
383 
384 	if( m_pStringToValPosMap != NULL )
385 	{
386 		StringToValPosMap::const_iterator it = m_pStringToValPosMap->find( rKey );
387 		if( it != m_pStringToValPosMap->end() )
388 		{
389 			const std::pair<int,int>& rValPair = it->second;
390 			int iValuePos = rValPair.first;
391 			int nValueLen = rValPair.second;
392 
393 			Reference< XInputStream > xIn = m_xSFA->openFileRead( m_aFileURL );
394 			if( xIn.is() )
395 			{
396 				Reference< XSeekable > xXSeekable( xIn, UNO_QUERY );
397 				if( xXSeekable.is() )
398 				{
399 					xXSeekable->seek( iValuePos );
400 
401 					Sequence< sal_Int8 > aData;
402 					sal_Int32 nRead = xIn->readBytes( aData, nValueLen );
403 					if( nRead == nValueLen )
404 					{
405 						const char* pData = (const sal_Char*)aData.getConstArray();
406 						rValue.copyToBuffer( pData, nValueLen );
407 						bSuccess = true;
408 					}
409 				}
410 				xIn->closeInput();
411 			}
412 		}
413 	}
414 
415 	else if( m_pStringToDataMap != NULL )
416 	{
417 		StringToDataMap::const_iterator it = m_pStringToDataMap->find( rKey );
418 		if( it != m_pStringToDataMap->end() )
419 		{
420 			const rtl::OString& rValueStr = it->second;
421 			int nValueLen = rValueStr.getLength();
422 			const char* pData = rValueStr.getStr();
423 			rValue.copyToBuffer( pData, nValueLen );
424 			bSuccess = true;
425 		}
426 	}
427 
428 	}
429 	catch( Exception & )
430 	{
431 		bSuccess = false;
432 	}
433 
434 	return bSuccess;
435 }
436 
437 bool DBHelp::startIteration( void )
438 {
439 	bool bSuccess = false;
440 
441 	sal_Int32 nSize = m_xSFA->getSize( m_aFileURL );
442 
443 	Reference< XInputStream > xIn = m_xSFA->openFileRead( m_aFileURL );
444 	if( xIn.is() )
445 	{
446 		m_nItRead = xIn->readBytes( m_aItData, nSize );
447 		if( m_nItRead == nSize )
448 		{
449 			bSuccess = true;
450 			m_pItData = (const char*)m_aItData.getConstArray();
451 			m_iItPos = 0;
452 		}
453 		else
454 		{
455 			stopIteration();
456 		}
457 	}
458 
459 	return bSuccess;
460 }
461 
462 bool DBHelp::getNextKeyAndValue( DBData& rKey, DBData& rValue )
463 {
464 	bool bSuccess = false;
465 
466 	if( m_iItPos < m_nItRead )
467 	{
468 		if( implReadLenAndData( m_pItData, m_iItPos, rKey ) )
469 		{
470 			if( implReadLenAndData( m_pItData, m_iItPos, rValue ) )
471 				bSuccess = true;
472 		}
473 	}
474 
475 	return bSuccess;
476 }
477 
478 void DBHelp::stopIteration( void )
479 {
480 	m_aItData = Sequence<sal_Int8>();
481 	m_pItData = NULL;
482 	m_nItRead = -1;
483 	m_iItPos = -1;
484 }
485 
486 
487 Db::Db()
488 {
489     db_internal::check_error( db_create(&m_pDBP,0,0),"Db::Db" );
490 	m_pDBHelp = NULL;
491 }
492 
493 
494 Db::~Db()
495 {
496     if (m_pDBP)
497     {
498         // should not happen
499         // TODO: add assert
500     }
501 
502 	delete m_pDBHelp;
503 }
504 
505 
506 int Db::close(u_int32_t flags)
507 {
508     int error = m_pDBP->close(m_pDBP,flags);
509     m_pDBP = 0;
510     return db_internal::check_error(error,"Db::close");
511 }
512 
513 int Db::open(DB_TXN *txnid,
514 			 const char *file,
515 			 const char *database,
516 			 DBTYPE type,
517 			 u_int32_t flags,
518 			 int mode)
519 {
520     int err = m_pDBP->open(m_pDBP,txnid,file,database,type,flags,mode);
521     return db_internal::check_error( err,"Db::open" );
522 }
523 
524 int Db::open(DB_TXN *txnid,
525              ::rtl::OUString const & fileURL,
526              DBTYPE type,
527              u_int32_t flags,
528              int mode)
529 {
530     ::rtl::OUString ouPath;
531     ::osl::FileBase::getSystemPathFromFileURL(fileURL, ouPath);
532     const ::rtl::OString sPath = ::rtl::OUStringToOString(ouPath, osl_getThreadTextEncoding());
533     return open(txnid, sPath.getStr(), 0, type, flags, mode);
534 }
535 
536 
537 
538 int Db::get(DB_TXN *txnid, Dbt *key, Dbt *data, u_int32_t flags)
539 {
540     int err = m_pDBP->get(m_pDBP,txnid,key,data,flags);
541 
542     // these are non-exceptional outcomes
543     if (err != DB_NOTFOUND && err != DB_KEYEMPTY)
544         db_internal::check_error( err,"Db::get" );
545 
546     return err;
547 }
548 
549 int Db::cursor(DB_TXN *txnid, Dbc **cursorp, u_int32_t flags)
550 {
551     DBC * dbc = 0;
552     int error = m_pDBP->cursor(m_pDBP,txnid,&dbc,flags);
553 
554     if (!db_internal::check_error(error,"Db::cursor"))
555         *cursorp = new Dbc(dbc);
556 
557     return error;
558 }
559 
560 //----------------------------------------------------------------------------
561 
562 Dbc::Dbc(DBC * dbc)
563 : m_pDBC(dbc)
564 {
565 }
566 
567 Dbc::~Dbc()
568 {
569 }
570 
571 int Dbc::close()
572 {
573     int err = m_pDBC->c_close(m_pDBC);
574     delete this;
575     return db_internal::check_error( err,"Dbcursor::close" );
576 }
577 
578 int Dbc::get(Dbt *key, Dbt *data, u_int32_t flags)
579 {
580     int err = m_pDBC->c_get(m_pDBC,key,data,flags);
581 
582     // these are non-exceptional outcomes
583     if (err != DB_NOTFOUND && err != DB_KEYEMPTY)
584         db_internal::check_error( err, "Dbcursor::get" );
585 
586     return err;
587 }
588 
589 //----------------------------------------------------------------------------
590 
591 
592 Dbt::Dbt()
593 {
594     using namespace std;
595     DBT * thispod = this;
596     memset(thispod, 0, sizeof *thispod);
597 }
598 
599 
600 Dbt::Dbt(void *data_arg, u_int32_t size_arg)
601 {
602     using namespace std;
603     DBT * thispod = this;
604     memset(thispod, 0, sizeof *thispod);
605     this->set_data(data_arg);
606     this->set_size(size_arg);
607 }
608 
609 /*
610 Dbt::Dbt(const Dbt & other)
611 {
612     using namespace std;
613 	const DBT *otherpod = &other;
614 	DBT *thispod = this;
615 	memcpy(thispod, otherpod, sizeof *thispod);
616 }
617 
618 Dbt& Dbt::operator = (const Dbt & other)
619 {
620 	if (this != &other)
621     {
622         using namespace std;
623         const DBT *otherpod = &other;
624         DBT *thispod = this;
625         memcpy(thispod, otherpod, sizeof *thispod);
626 	}
627 	return *this;
628 }
629 */
630 
631 Dbt::~Dbt()
632 {
633 }
634 
635 void * Dbt::get_data() const
636 {
637     return this->data;
638 }
639 
640 void Dbt::set_data(void *value)
641 {
642     this->data = value;
643 }
644 
645 u_int32_t Dbt::get_size() const
646 {
647     return this->size;
648 }
649 
650 void Dbt::set_size(u_int32_t value)
651 {
652     this->size = value;
653 }
654 
655 void Dbt::set_flags(u_int32_t value)
656 {
657     this->flags = value;
658 }
659 
660 //----------------------------------------------------------------------------
661 /*
662 void db_internal::raise_error(int dberr, const char * where)
663 {
664     if (!where) where = "<unknown>";
665 
666     const char * dberrmsg = db_strerror(dberr);
667     if (!dberrmsg || !*dberrmsg) dberrmsg = "<unknown DB error>";
668 
669     rtl::OString msg = where;
670     msg += ": ";
671     msg += dberrmsg;
672 
673     throw DbException(msg);
674 }
675 */
676 
677 //----------------------------------------------------------------------------
678 } // namespace ecomp
679 
680