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