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