xref: /trunk/main/xmlhelp/source/cxxhelp/provider/db.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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