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