diff options
233 files changed, 34236 insertions, 5858 deletions
diff --git a/.gitignore b/.gitignore index 95152ce4fb..b11949171e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,14 +10,20 @@ src/test_bitcoin *.o *.patch .bitcoin -#compilation and Qt preprocessor part + +# Compilation and Qt preprocessor part *.qm Makefile bitcoin-qt -#resources cpp + +# Resources cpp qrc_*.cpp -#qt creator + +# Qt creator *.pro.user -#mac specific + +# Mac specific .DS_Store build + +!src/leveldb-*/Makefile diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index a73a2c379b..1808fcc3dc 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -1,6 +1,6 @@ TEMPLATE = app TARGET = bitcoin-qt -VERSION = 0.7.0 +VERSION = 0.7.1 INCLUDEPATH += src src/json src/qt DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE CONFIG += no_include_pwd @@ -90,6 +90,23 @@ contains(BITCOIN_NEED_QT_PLUGINS, 1) { QTPLUGIN += qcncodecs qjpcodecs qtwcodecs qkrcodecs qtaccessiblewidgets } +INCLUDEPATH += src/leveldb/include src/leveldb/helpers +LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a +!windows { + genleveldb.commands = cd $$PWD/src/leveldb ; $(MAKE) libleveldb.a libmemenv.a +} else { + # make an educated guess about what the ranlib command is called + isEmpty(QMAKE_RANLIB) { + QMAKE_RANLIB = $$replace(QMAKE_STRIP, strip, ranlib) + } + genleveldb.commands = cd $$PWD/src/leveldb ; CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$$BOOST_INCLUDE_PATH" LDFLAGS="-L$$BOOST_LIB_PATH" $(MAKE) libleveldb.a libmemenv.a ; $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a ; $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a +} +genleveldb.target = $$PWD/src/leveldb/libleveldb.a +genleveldb.depends = FORCE +PRE_TARGETDEPS += $$PWD/src/leveldb/libleveldb.a +QMAKE_EXTRA_TARGETS += genleveldb +# Gross ugly hack that depends on qmake internals, unfortunately there's no other way to do it. +QMAKE_CLEAN += $$PWD/src/leveldb/libleveldb.a; cd $$PWD/src/leveldb ; $(MAKE) clean # regenerate src/build.h !windows|contains(USE_BUILD_INFO, 1) { @@ -125,11 +142,11 @@ HEADERS += src/qt/bitcoingui.h \ src/util.h \ src/uint256.h \ src/serialize.h \ - src/strlcpy.h \ src/main.h \ src/net.h \ src/key.h \ src/db.h \ + src/txdb.h \ src/walletdb.h \ src/script.h \ src/init.h \ @@ -175,7 +192,9 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/rpcconsole.h \ src/version.h \ src/netbase.h \ - src/clientversion.h + src/clientversion.h \ + src/txdb.h \ + src/leveldb.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ @@ -235,7 +254,9 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/notificator.cpp \ src/qt/qtipcserver.cpp \ src/qt/rpcconsole.cpp \ - src/noui.cpp + src/noui.cpp \ + src/leveldb.cpp \ + src/txdb.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/doc/README b/doc/README index bc6468e673..b20da7fb05 100644 --- a/doc/README +++ b/doc/README @@ -1,4 +1,4 @@ -Bitcoin 0.7.0 BETA +Bitcoin 0.7.1 BETA Copyright (c) 2009-2012 Bitcoin Developers Distributed under the MIT/X11 software license, see the accompanying diff --git a/doc/README_windows.txt b/doc/README_windows.txt index 4d9b4b20a4..7ec86d7131 100644 --- a/doc/README_windows.txt +++ b/doc/README_windows.txt @@ -1,4 +1,4 @@ -Bitcoin 0.7.0 BETA
+Bitcoin 0.7.1 BETA
Copyright (c) 2009-2012 Bitcoin Developers
Distributed under the MIT/X11 software license, see the accompanying
diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index c66665d012..0693d535c3 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -1,7 +1,3 @@ -Code: src/strlcpy.h -Author: Todd C. Miller <Todd.Miller@courtesan.com> -License: ISC - Icon: src/qt/res/icons/clock*.png, src/qt/res/icons/tx*.png, src/qt/res/src/*.svg Designer: Wladimir van der Laan diff --git a/doc/release-notes.txt b/doc/release-notes.txt index 3c975af92f..b7f7dfb402 100644 --- a/doc/release-notes.txt +++ b/doc/release-notes.txt @@ -19,8 +19,55 @@ Ubuntu PPA version), then run the old version again with the -detachdb argument and shut it down; if you do not, then the new version will not be able to read the database files and will exit with an error. +Explanation of -detachdb (and the new "stop true" RPC command): +The Berkeley DB database library stores data in both ".dat" and +"log" files, so the database is always in a consistent state, +even in case of power failure or other sudden shutdown. The +format of the ".dat" files is portable between different +versions of Berkeley DB, but the "log" files are not-- even minor +version differences may have incompatible "log" files. The +-detachdb option moves any pending changes from the "log" files +to the "blkindex.dat" file for maximum compatibility, but makes +shutdown much slower. Note that the "wallet.dat" file is always +detached, and versions prior to 0.6.0 detached all databases +at shutdown. + +New features +------------ + +* Added a boolean argument to the RPC 'stop' command, if true sets + -detachdb to create standalone database .dat files before shutting down. + +* -salvagewallet command-line option, which moves any existing wallet.dat + to wallet.{timestamp}.dat and then attempts to salvage public/private + keys and master encryption keys (if the wallet is encrypted) into + a new wallet.dat. This should only be used if your wallet becomes + corrupted, and is not intended to replace regular wallet backups. + +* Import $DataDir/bootstrap.dat automatically, if it exists. + +Dependency changes +------------------ + +* Qt 4.8.2 for Windows builds + +* openssl 1.0.1c + Bug fixes --------- + +* When running -testnet, use RPC port 18332 by default. + +* Better detection and handling of corrupt wallet.dat and blkindex.dat files. + Previous versions would crash with a DB_RUNRECOVERY exception, this + version detects most problems and tells you how to recover if it + cannot recover itself. + * Fixed an uninitialized variable bug that could cause transactions to be reported out of order. +* Fixed a bug that could cause occasional crashes on exit. + +* Warn the user that they need to create fresh wallet backups after they + encrypt their wallet. + diff --git a/share/setup.nsi b/share/setup.nsi index f799987ed9..fd7451ea76 100644 --- a/share/setup.nsi +++ b/share/setup.nsi @@ -5,7 +5,7 @@ SetCompressor /SOLID lzma # General Symbol Definitions
!define REGKEY "SOFTWARE\$(^Name)"
-!define VERSION 0.7.0
+!define VERSION 0.7.1
!define COMPANY "Bitcoin project"
!define URL http://www.bitcoin.org/
@@ -45,13 +45,13 @@ Var StartMenuGroup !insertmacro MUI_LANGUAGE English
# Installer attributes
-OutFile bitcoin-0.7.0-win32-setup.exe
+OutFile bitcoin-0.7.1-win32-setup.exe
InstallDir $PROGRAMFILES\Bitcoin
CRCCheck on
XPStyle on
BrandingText " "
ShowInstDetails show
-VIProductVersion 0.7.0.3
+VIProductVersion 0.7.1.0
VIAddVersionKey ProductName Bitcoin
VIAddVersionKey ProductVersion "${VERSION}"
VIAddVersionKey CompanyName "${COMPANY}"
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 0704173e54..725037addc 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -256,6 +256,8 @@ static const CRPCCommand vRPCCommands[] = { "decoderawtransaction", &decoderawtransaction, false, false }, { "signrawtransaction", &signrawtransaction, false, false }, { "sendrawtransaction", &sendrawtransaction, false, false }, + { "gettxoutsetinfo", &gettxoutsetinfo, true, false }, + { "gettxout", &gettxout, true, false }, }; CRPCTable::CRPCTable() @@ -796,7 +798,7 @@ void ThreadRPCServer2(void* parg) } catch(boost::system::system_error &e) { - strerr = strprintf(_("An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); + strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); } try { @@ -1166,6 +1168,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]); if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true); if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true); + if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]); return params; } diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 89ab976f56..9290697664 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -187,5 +187,7 @@ extern json_spirit::Value settxfee(const json_spirit::Array& params, bool fHelp) extern json_spirit::Value getrawmempool(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp); #endif diff --git a/src/clientversion.h b/src/clientversion.h index 548105383c..2f695e5cc6 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -8,8 +8,8 @@ // These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 #define CLIENT_VERSION_MINOR 7 -#define CLIENT_VERSION_REVISION 0 -#define CLIENT_VERSION_BUILD 99 +#define CLIENT_VERSION_REVISION 1 +#define CLIENT_VERSION_BUILD 0 // Converts the parameter X to a string after macro replacement on X has been performed. // Don't merge these into one macro! diff --git a/src/db.cpp b/src/db.cpp index 278c82078a..cf96fe6e38 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -34,19 +34,14 @@ void CDBEnv::EnvShutdown() return; fDbEnvInit = false; - try - { - dbenv.close(0); - } - catch (const DbException& e) - { - printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno()); - } + int ret = dbenv.close(0); + if (ret != 0) + printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret); if (!fMockDb) DbEnv(0).remove(GetDataDir().string().c_str(), 0); } -CDBEnv::CDBEnv() : dbenv(0) +CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS) { } @@ -84,8 +79,8 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_) dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); dbenv.set_lg_bsize(1048576); dbenv.set_lg_max(10485760); - dbenv.set_lk_max_locks(10000); - dbenv.set_lk_max_objects(10000); + dbenv.set_lk_max_locks(40000); + dbenv.set_lk_max_objects(40000); dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug dbenv.set_flags(DB_AUTO_COMMIT, 1); dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); @@ -100,8 +95,8 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_) DB_RECOVER | nEnvFlags, S_IRUSR | S_IWUSR); - if (ret > 0) - return error("CDB() : error %d opening database environment", ret); + if (ret != 0) + return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret); fDbEnvInit = true; fMockDb = false; @@ -141,6 +136,69 @@ void CDBEnv::MakeMock() fMockDb = true; } +CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)) +{ + LOCK(cs_db); + assert(mapFileUseCount.count(strFile) == 0); + + Db db(&dbenv, 0); + int result = db.verify(strFile.c_str(), NULL, NULL, 0); + if (result == 0) + return VERIFY_OK; + else if (recoverFunc == NULL) + return RECOVER_FAIL; + + // Try to recover: + bool fRecovered = (*recoverFunc)(*this, strFile); + return (fRecovered ? RECOVER_OK : RECOVER_FAIL); +} + +bool CDBEnv::Salvage(std::string strFile, bool fAggressive, + std::vector<CDBEnv::KeyValPair >& vResult) +{ + LOCK(cs_db); + assert(mapFileUseCount.count(strFile) == 0); + + u_int32_t flags = DB_SALVAGE; + if (fAggressive) flags |= DB_AGGRESSIVE; + + stringstream strDump; + + Db db(&dbenv, 0); + int result = db.verify(strFile.c_str(), NULL, &strDump, flags); + if (result != 0) + { + printf("ERROR: db salvage failed\n"); + return false; + } + + // Format of bdb dump is ascii lines: + // header lines... + // HEADER=END + // hexadecimal key + // hexadecimal value + // ... repeated + // DATA=END + + string strLine; + while (!strDump.eof() && strLine != "HEADER=END") + getline(strDump, strLine); // Skip past header + + std::string keyHex, valueHex; + while (!strDump.eof() && keyHex != "DATA=END") + { + getline(strDump, keyHex); + if (keyHex != "DATA_END") + { + getline(strDump, valueHex); + vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex))); + } + } + + return (result == 0); +} + + void CDBEnv::CheckpointLSN(std::string strFile) { dbenv.txn_checkpoint(0, 0, 0); @@ -186,12 +244,12 @@ CDB::CDB(const char *pszFile, const char* pszMode) : ret = pdb->open(NULL, // Txn pointer fMockDb ? NULL : pszFile, // Filename - "main", // Logical db name + fMockDb ? pszFile : "main", // Logical db name DB_BTREE, // Database type nFlags, // Flags 0); - if (ret > 0) + if (ret != 0) { delete pdb; pdb = NULL; @@ -215,20 +273,16 @@ CDB::CDB(const char *pszFile, const char* pszMode) : static bool IsChainFile(std::string strFile) { - if (strFile == "blkindex.dat") + if (strFile == "coins.dat" || strFile == "blktree.dat") return true; return false; } -void CDB::Close() +void CDB::Flush() { - if (!pdb) - return; if (activeTxn) - activeTxn->abort(); - activeTxn = NULL; - pdb = NULL; + return; // Flush database activity from memory pool to disk log unsigned int nMinutes = 0; @@ -240,6 +294,18 @@ void CDB::Close() nMinutes = 5; bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); +} + +void CDB::Close() +{ + if (!pdb) + return; + if (activeTxn) + activeTxn->abort(); + activeTxn = NULL; + pdb = NULL; + + Flush(); { LOCK(bitdb.cs_db); @@ -262,6 +328,15 @@ void CDBEnv::CloseDb(const string& strFile) } } +bool CDBEnv::RemoveDb(const string& strFile) +{ + this->CloseDb(strFile); + + LOCK(cs_db); + int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT); + return (rc == 0); +} + bool CDB::Rewrite(const string& strFile, const char* pszSkip) { while (!fShutdown) @@ -407,356 +482,6 @@ void CDBEnv::Flush(bool fShutdown) -// -// CTxDB -// - -bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex) -{ - assert(!fClient); - txindex.SetNull(); - return Read(make_pair(string("tx"), hash), txindex); -} - -bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex) -{ - assert(!fClient); - return Write(make_pair(string("tx"), hash), txindex); -} - -bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight) -{ - assert(!fClient); - - // Add to tx index - uint256 hash = tx.GetHash(); - CTxIndex txindex(pos, tx.vout.size()); - return Write(make_pair(string("tx"), hash), txindex); -} - -bool CTxDB::EraseTxIndex(const CTransaction& tx) -{ - assert(!fClient); - uint256 hash = tx.GetHash(); - - return Erase(make_pair(string("tx"), hash)); -} - -bool CTxDB::ContainsTx(uint256 hash) -{ - assert(!fClient); - return Exists(make_pair(string("tx"), hash)); -} - -bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex) -{ - assert(!fClient); - tx.SetNull(); - if (!ReadTxIndex(hash, txindex)) - return false; - return (tx.ReadFromDisk(txindex.pos)); -} - -bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx) -{ - CTxIndex txindex; - return ReadDiskTx(hash, tx, txindex); -} - -bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex) -{ - return ReadDiskTx(outpoint.hash, tx, txindex); -} - -bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx) -{ - CTxIndex txindex; - return ReadDiskTx(outpoint.hash, tx, txindex); -} - -bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) -{ - return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex); -} - -bool CTxDB::ReadHashBestChain(uint256& hashBestChain) -{ - return Read(string("hashBestChain"), hashBestChain); -} - -bool CTxDB::WriteHashBestChain(uint256 hashBestChain) -{ - return Write(string("hashBestChain"), hashBestChain); -} - -bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) -{ - return Read(string("bnBestInvalidWork"), bnBestInvalidWork); -} - -bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork) -{ - return Write(string("bnBestInvalidWork"), bnBestInvalidWork); -} - -CBlockIndex static * InsertBlockIndex(uint256 hash) -{ - if (hash == 0) - return NULL; - - // Return existing - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - return (*mi).second; - - // Create new - CBlockIndex* pindexNew = new CBlockIndex(); - if (!pindexNew) - throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); - mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); - - return pindexNew; -} - -bool CTxDB::LoadBlockIndex() -{ - if (!LoadBlockIndexGuts()) - return false; - - if (fRequestShutdown) - return true; - - // Calculate bnChainWork - vector<pair<int, CBlockIndex*> > vSortedByHeight; - vSortedByHeight.reserve(mapBlockIndex.size()); - BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) - { - CBlockIndex* pindex = item.second; - vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); - } - sort(vSortedByHeight.begin(), vSortedByHeight.end()); - BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) - { - CBlockIndex* pindex = item.second; - pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); - } - - // Load hashBestChain pointer to end of best chain - if (!ReadHashBestChain(hashBestChain)) - { - if (pindexGenesisBlock == NULL) - return true; - return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded"); - } - if (!mapBlockIndex.count(hashBestChain)) - return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index"); - pindexBest = mapBlockIndex[hashBestChain]; - nBestHeight = pindexBest->nHeight; - bnBestChainWork = pindexBest->bnChainWork; - printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n", - hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, - DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); - - // Load bnBestInvalidWork, OK if it doesn't exist - ReadBestInvalidWork(bnBestInvalidWork); - - // Verify blocks in the best chain - int nCheckLevel = GetArg("-checklevel", 1); - int nCheckDepth = GetArg( "-checkblocks", 2500); - if (nCheckDepth == 0) - nCheckDepth = 1000000000; // suffices until the year 19000 - if (nCheckDepth > nBestHeight) - nCheckDepth = nBestHeight; - printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); - CBlockIndex* pindexFork = NULL; - map<pair<unsigned int, unsigned int>, CBlockIndex*> mapBlockPos; - for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) - { - if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) - break; - CBlock block; - if (!block.ReadFromDisk(pindex)) - return error("LoadBlockIndex() : block.ReadFromDisk failed"); - // check level 1: verify block validity - if (nCheckLevel>0 && !block.CheckBlock()) - { - printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - pindexFork = pindex->pprev; - } - // check level 2: verify transaction index validity - if (nCheckLevel>1) - { - pair<unsigned int, unsigned int> pos = make_pair(pindex->nFile, pindex->nBlockPos); - mapBlockPos[pos] = pindex; - BOOST_FOREACH(const CTransaction &tx, block.vtx) - { - uint256 hashTx = tx.GetHash(); - CTxIndex txindex; - if (ReadTxIndex(hashTx, txindex)) - { - // check level 3: checker transaction hashes - if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos) - { - // either an error or a duplicate transaction - CTransaction txFound; - if (!txFound.ReadFromDisk(txindex.pos)) - { - printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str()); - pindexFork = pindex->pprev; - } - else - if (txFound.GetHash() != hashTx) // not a duplicate tx - { - printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str()); - pindexFork = pindex->pprev; - } - } - // check level 4: check whether spent txouts were spent within the main chain - unsigned int nOutput = 0; - if (nCheckLevel>3) - { - BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent) - { - if (!txpos.IsNull()) - { - pair<unsigned int, unsigned int> posFind = make_pair(txpos.nFile, txpos.nBlockPos); - if (!mapBlockPos.count(posFind)) - { - printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str()); - pindexFork = pindex->pprev; - } - // check level 6: check whether spent txouts were spent by a valid transaction that consume them - if (nCheckLevel>5) - { - CTransaction txSpend; - if (!txSpend.ReadFromDisk(txpos)) - { - printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput); - pindexFork = pindex->pprev; - } - else if (!txSpend.CheckTransaction()) - { - printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput); - pindexFork = pindex->pprev; - } - else - { - bool fFound = false; - BOOST_FOREACH(const CTxIn &txin, txSpend.vin) - if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput) - fFound = true; - if (!fFound) - { - printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput); - pindexFork = pindex->pprev; - } - } - } - } - nOutput++; - } - } - } - // check level 5: check whether all prevouts are marked spent - if (nCheckLevel>4) - { - BOOST_FOREACH(const CTxIn &txin, tx.vin) - { - CTxIndex txindex; - if (ReadTxIndex(txin.prevout.hash, txindex)) - if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull()) - { - printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str()); - pindexFork = pindex->pprev; - } - } - } - } - } - } - if (pindexFork && !fRequestShutdown) - { - // Reorg back to the fork - printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight); - CBlock block; - if (!block.ReadFromDisk(pindexFork)) - return error("LoadBlockIndex() : block.ReadFromDisk failed"); - CTxDB txdb; - block.SetBestChain(txdb, pindexFork); - } - - return true; -} - - - -bool CTxDB::LoadBlockIndexGuts() -{ - // Get database cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) - return false; - - // Load mapBlockIndex - unsigned int fFlags = DB_SET_RANGE; - loop - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - if (fFlags == DB_SET_RANGE) - ssKey << make_pair(string("blockindex"), uint256(0)); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); - fFlags = DB_NEXT; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - return false; - - // Unserialize - - try { - string strType; - ssKey >> strType; - if (strType == "blockindex" && !fRequestShutdown) - { - CDiskBlockIndex diskindex; - ssValue >> diskindex; - - // Construct block index object - CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); - pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); - pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); - pindexNew->nFile = diskindex.nFile; - pindexNew->nBlockPos = diskindex.nBlockPos; - pindexNew->nHeight = diskindex.nHeight; - pindexNew->nVersion = diskindex.nVersion; - pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; - pindexNew->nTime = diskindex.nTime; - pindexNew->nBits = diskindex.nBits; - pindexNew->nNonce = diskindex.nNonce; - - // Watch for genesis block - if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) - pindexGenesisBlock = pindexNew; - - if (!pindexNew->CheckIndex()) - return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); - } - else - { - break; // if shutdown requested or finished loading block index - } - } // try - catch (std::exception &e) { - return error("%s() : deserialize error", __PRETTY_FUNCTION__); - } - } - pcursor->close(); - - return true; -} @@ -17,10 +17,8 @@ class CAddress; class CAddrMan; class CBlockLocator; class CDiskBlockIndex; -class CDiskTxPos; class CMasterKey; class COutPoint; -class CTxIndex; class CWallet; class CWalletTx; @@ -50,6 +48,25 @@ public: ~CDBEnv(); void MakeMock(); bool IsMock() { return fMockDb; }; + + /* + * Verify that database file strFile is OK. If it is not, + * call the callback to try to recover. + * This must be called BEFORE strFile is opened. + * Returns true if strFile is OK. + */ + enum VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL }; + VerifyResult Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)); + /* + * Salvage data from a file that Verify says is bad. + * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation). + * Appends binary key/value pairs to vResult, returns true if successful. + * NOTE: reads the entire database into memory, so cannot be used + * for huge databases. + */ + typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair; + bool Salvage(std::string strFile, bool fAggressive, std::vector<KeyValPair>& vResult); + bool Open(boost::filesystem::path pathEnv_); void Close(); void Flush(bool fShutdown); @@ -58,6 +75,7 @@ public: bool GetDetach() { return fDetachDB; } void CloseDb(const std::string& strFile); + bool RemoveDb(const std::string& strFile); DbTxn *TxnBegin(int flags=DB_TXN_WRITE_NOSYNC) { @@ -84,6 +102,7 @@ protected: explicit CDB(const char* pszFile, const char* pszMode="r+"); ~CDB() { Close(); } public: + void Flush(); void Close(); private: CDB(const CDB&); @@ -296,36 +315,6 @@ public: -/** Access to the transaction database (blkindex.dat) */ -class CTxDB : public CDB -{ -public: - CTxDB(const char* pszMode="r+") : CDB("blkindex.dat", pszMode) { } -private: - CTxDB(const CTxDB&); - void operator=(const CTxDB&); -public: - bool ReadTxIndex(uint256 hash, CTxIndex& txindex); - bool UpdateTxIndex(uint256 hash, const CTxIndex& txindex); - bool AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight); - bool EraseTxIndex(const CTransaction& tx); - bool ContainsTx(uint256 hash); - bool ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex); - bool ReadDiskTx(uint256 hash, CTransaction& tx); - bool ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex); - bool ReadDiskTx(COutPoint outpoint, CTransaction& tx); - bool WriteBlockIndex(const CDiskBlockIndex& blockindex); - bool ReadHashBestChain(uint256& hashBestChain); - bool WriteHashBestChain(uint256 hashBestChain); - bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); - bool WriteBestInvalidWork(CBigNum bnBestInvalidWork); - bool LoadBlockIndex(); -private: - bool LoadBlockIndexGuts(); -}; - - - /** Access to the (IP) address database (peers.dat) */ class CAddrDB diff --git a/src/init.cpp b/src/init.cpp index 7e2cda6297..4d5720306d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "db.h" +#include "txdb.h" #include "walletdb.h" #include "bitcoinrpc.h" #include "net.h" @@ -50,6 +50,8 @@ void StartShutdown() #endif } +static CCoinsViewDB *pcoinsdbview; + void Shutdown(void* parg) { static CCriticalSection cs_Shutdown; @@ -74,6 +76,14 @@ void Shutdown(void* parg) nTransactionsUpdated++; bitdb.Flush(false); StopNode(); + { + LOCK(cs_main); + pcoinsTip->Flush(); + pblocktree->Flush(); + delete pcoinsTip; + delete pcoinsdbview; + delete pblocktree; + } bitdb.Flush(true); boost::filesystem::remove(GetPidFile()); UnregisterWallet(pwalletMain); @@ -144,7 +154,7 @@ bool AppInit(int argc, char* argv[]) strUsage += "\n" + HelpMessage(); - fprintf(stderr, "%s", strUsage.c_str()); + fprintf(stdout, "%s", strUsage.c_str()); return false; } @@ -279,6 +289,7 @@ std::string HelpMessage() " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n" + " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + + " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + " -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + " -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + " -loadblock=<file> " + _("Imports blocks from external blk000?.dat file") + "\n" + @@ -297,6 +308,7 @@ std::string HelpMessage() return strUsage; } + /** Initialize bitcoin. * @pre Parameters should be parsed and config file should be read. */ @@ -379,6 +391,11 @@ bool AppInit2() SoftSetBoolArg("-discover", false); } + if (GetBoolArg("-salvagewallet")) { + // Rewrite just private keys: rescan to find transactions + SoftSetBoolArg("-rescan", true); + } + // ********************************************************* Step 3: parameter-to-internal-flags fDebug = GetBoolArg("-debug"); @@ -434,13 +451,15 @@ bool AppInit2() // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log + std::string strDataDir = GetDataDir().string(); + // Make sure only a single Bitcoin process is using the data directory. boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. if (file) fclose(file); static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); if (!lock.try_lock()) - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().string().c_str())); + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); #if !defined(WIN32) && !defined(QT_GUI) if (fDaemon) @@ -472,7 +491,7 @@ bool AppInit2() if (!fLogTimestamps) printf("Startup time: %s\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); printf("Default data directory %s\n", GetDefaultDataDir().string().c_str()); - printf("Used data directory %s\n", GetDataDir().string().c_str()); + printf("Used data directory %s\n", strDataDir.c_str()); std::ostringstream strErrors; if (fDaemon) @@ -480,7 +499,41 @@ bool AppInit2() int64 nStart; - // ********************************************************* Step 5: network initialization + // ********************************************************* Step 5: verify database integrity + + uiInterface.InitMessage(_("Verifying database integrity...")); + + if (!bitdb.Open(GetDataDir())) + { + string msg = strprintf(_("Error initializing database environment %s!" + " To recover, BACKUP THAT DIRECTORY, then remove" + " everything from it except for wallet.dat."), strDataDir.c_str()); + return InitError(msg); + } + + if (GetBoolArg("-salvagewallet")) + { + // Recover readable keypairs: + if (!CWalletDB::Recover(bitdb, "wallet.dat", true)) + return false; + } + + if (filesystem::exists(GetDataDir() / "wallet.dat")) + { + CDBEnv::VerifyResult r = bitdb.Verify("wallet.dat", CWalletDB::Recover); + if (r == CDBEnv::RECOVER_OK) + { + string msg = strprintf(_("Warning: wallet.dat corrupt, data salvaged!" + " Original wallet.dat saved as wallet.{timestamp}.bak in %s; if" + " your balance or transactions are incorrect you should" + " restore from a backup."), strDataDir.c_str()); + uiInterface.ThreadSafeMessageBox(msg, _("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + } + if (r == CDBEnv::RECOVER_FAIL) + return InitError(_("wallet.dat corrupt, salvage failed")); + } + + // ********************************************************* Step 6: network initialization int nSocksVersion = GetArg("-socks", 5); @@ -586,21 +639,25 @@ bool AppInit2() BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) AddOneShot(strDest); - // ********************************************************* Step 6: load blockchain + // ********************************************************* Step 7: load blockchain - if (GetBoolArg("-loadblockindextest")) + if (!bitdb.Open(GetDataDir())) { - CTxDB txdb("r"); - txdb.LoadBlockIndex(); - PrintBlockTree(); - return false; + string msg = strprintf(_("Error initializing database environment %s!" + " To recover, BACKUP THAT DIRECTORY, then remove" + " everything from it except for wallet.dat."), strDataDir.c_str()); + return InitError(msg); } uiInterface.InitMessage(_("Loading block index...")); printf("Loading block index...\n"); nStart = GetTimeMillis(); + pblocktree = new CBlockTreeDB(); + pcoinsdbview = new CCoinsViewDB(); + pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + if (!LoadBlockIndex()) - strErrors << _("Error loading blkindex.dat") << "\n"; + return InitError(_("Error loading blkindex.dat")); // as LoadBlockIndex can take several minutes, it's possible the user // requested to kill bitcoin-qt during the last operation. If so, exit. @@ -641,18 +698,24 @@ bool AppInit2() return false; } - // ********************************************************* Step 7: load wallet + // ********************************************************* Step 8: load wallet uiInterface.InitMessage(_("Loading wallet...")); printf("Loading wallet...\n"); nStart = GetTimeMillis(); bool fFirstRun = true; pwalletMain = new CWallet("wallet.dat"); - int nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); + DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); if (nLoadWalletRet != DB_LOAD_OK) { if (nLoadWalletRet == DB_CORRUPT) strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n"; + else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) + { + string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data" + " or address book entries might be missing or incorrect.")); + uiInterface.ThreadSafeMessageBox(msg, _("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + } else if (nLoadWalletRet == DB_TOO_NEW) strErrors << _("Error loading wallet.dat: Wallet requires newer version of Bitcoin") << "\n"; else if (nLoadWalletRet == DB_NEED_REWRITE) @@ -718,33 +781,22 @@ bool AppInit2() printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); } - // ********************************************************* Step 8: import blocks + // ********************************************************* Step 9: import blocks + + // scan for better chains in the block chain database, that are not yet connected in the active best chain + uiInterface.InitMessage(_("Importing blocks from block database...")); + if (!ConnectBestBlock()) + strErrors << "Failed to connect best block"; + std::vector<boost::filesystem::path> *vPath = new std::vector<boost::filesystem::path>(); if (mapArgs.count("-loadblock")) { - uiInterface.InitMessage(_("Importing blockchain data file.")); - BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) - { - FILE *file = fopen(strFile.c_str(), "rb"); - if (file) - LoadExternalBlockFile(file); - } - } - - filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; - if (filesystem::exists(pathBootstrap)) { - uiInterface.InitMessage(_("Importing bootstrap blockchain data file.")); - - FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); - if (file) { - filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; - LoadExternalBlockFile(file); - RenameOver(pathBootstrap, pathBootstrapOld); - } + vPath->push_back(strFile); } + NewThread(ThreadImport, vPath); - // ********************************************************* Step 9: load peers + // ********************************************************* Step 10: load peers uiInterface.InitMessage(_("Loading addresses...")); printf("Loading addresses...\n"); @@ -759,7 +811,7 @@ bool AppInit2() printf("Loaded %i addresses from peers.dat %"PRI64d"ms\n", addrman.size(), GetTimeMillis() - nStart); - // ********************************************************* Step 10: start node + // ********************************************************* Step 11: start node if (!CheckDiskSpace()) return false; @@ -779,7 +831,7 @@ bool AppInit2() if (fServer) NewThread(ThreadRPCServer, NULL); - // ********************************************************* Step 11: finished + // ********************************************************* Step 12: finished uiInterface.InitMessage(_("Done loading")); printf("Done loading\n"); @@ -799,4 +851,3 @@ bool AppInit2() return true; } - diff --git a/src/irc.cpp b/src/irc.cpp index ec7eea3cf4..17d5ff1a5a 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -5,9 +5,10 @@ #include "irc.h" #include "net.h" -#include "strlcpy.h" #include "base58.h" +#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith() + using namespace std; using namespace boost; @@ -324,30 +325,27 @@ void ThreadIRCSeed2(void* parg) if (vWords.size() < 2) continue; - char pszName[10000]; - pszName[0] = '\0'; + std::string strName; if (vWords[1] == "352" && vWords.size() >= 8) { // index 7 is limited to 16 characters // could get full length name at index 10, but would be different from join messages - strlcpy(pszName, vWords[7].c_str(), sizeof(pszName)); + strName = vWords[7].c_str(); printf("IRC got who\n"); } if (vWords[1] == "JOIN" && vWords[0].size() > 1) { // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname - strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName)); - if (strchr(pszName, '!')) - *strchr(pszName, '!') = '\0'; + strName = vWords[0].substr(1, vWords[0].find('!', 1) - 1); printf("IRC got join\n"); } - if (pszName[0] == 'u') + if (boost::algorithm::starts_with(strName, "u")) { CAddress addr; - if (DecodeAddress(pszName, addr)) + if (DecodeAddress(strName, addr)) { addr.nTime = GetAdjustedTime(); if (addrman.Add(addr, addrConnect, 51 * 60)) diff --git a/src/key.cpp b/src/key.cpp index 76c45d0635..20114e6bb2 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -120,9 +120,9 @@ err: return ret; } -void CKey::SetCompressedPubKey() +void CKey::SetCompressedPubKey(bool fCompressed) { - EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED); + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); fCompressedPubKey = true; } @@ -186,10 +186,24 @@ void CKey::MakeNewKey(bool fCompressed) bool CKey::SetPrivKey(const CPrivKey& vchPrivKey) { const unsigned char* pbegin = &vchPrivKey[0]; - if (!d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) - return false; - fSet = true; - return true; + if (d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) + { + // In testing, d2i_ECPrivateKey can return true + // but fill in pkey with a key that fails + // EC_KEY_check_key, so: + if (EC_KEY_check_key(pkey)) + { + fSet = true; + return true; + } + } + // If vchPrivKey data is bad d2i_ECPrivateKey() can + // leave pkey in a state where calling EC_KEY_free() + // crashes. To avoid that, set pkey to NULL and + // leak the memory (a leak is better than a crash) + pkey = NULL; + Reset(); + return false; } bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed) @@ -245,12 +259,16 @@ CPrivKey CKey::GetPrivKey() const bool CKey::SetPubKey(const CPubKey& vchPubKey) { const unsigned char* pbegin = &vchPubKey.vchPubKey[0]; - if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size())) - return false; - fSet = true; - if (vchPubKey.vchPubKey.size() == 33) - SetCompressedPubKey(); - return true; + if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size())) + { + fSet = true; + if (vchPubKey.vchPubKey.size() == 33) + SetCompressedPubKey(); + return true; + } + pkey = NULL; + Reset(); + return false; } CPubKey CKey::GetPubKey() const @@ -377,6 +395,9 @@ bool CKey::IsValid() if (!fSet) return false; + if (!EC_KEY_check_key(pkey)) + return false; + bool fCompr; CSecret secret = GetSecret(fCompr); CKey key2; @@ -113,9 +113,8 @@ protected: bool fSet; bool fCompressedPubKey; - void SetCompressedPubKey(); - public: + void SetCompressedPubKey(bool fCompressed = true); void Reset(); diff --git a/src/leveldb.cpp b/src/leveldb.cpp new file mode 100644 index 0000000000..e8a0fbe874 --- /dev/null +++ b/src/leveldb.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "leveldb.h" +#include "util.h" + +#include <leveldb/env.h> +#include <leveldb/cache.h> +#include <leveldb/filter_policy.h> +#include <memenv/memenv.h> + +#include <boost/filesystem.hpp> + +static leveldb::Options GetOptions() { + leveldb::Options options; + int nCacheSizeMB = GetArg("-dbcache", 25); + options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576); + options.filter_policy = leveldb::NewBloomFilterPolicy(10); + options.compression = leveldb::kNoCompression; + return options; +} + +CLevelDB::CLevelDB(const boost::filesystem::path &path, bool fMemory) { + penv = NULL; + readoptions.verify_checksums = true; + iteroptions.verify_checksums = true; + iteroptions.fill_cache = false; + syncoptions.sync = true; + options = GetOptions(); + options.create_if_missing = true; + if (fMemory) { + penv = leveldb::NewMemEnv(leveldb::Env::Default()); + options.env = penv; + } else { + boost::filesystem::create_directory(path); + printf("Opening LevelDB in %s\n", path.string().c_str()); + } + leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); + if (!status.ok()) + throw std::runtime_error(strprintf("CLevelDB(): error opening database environment %s", status.ToString().c_str())); + printf("Opened LevelDB successfully\n"); +} + +CLevelDB::~CLevelDB() { + delete pdb; + pdb = NULL; + delete options.filter_policy; + options.filter_policy = NULL; + delete options.block_cache; + options.block_cache = NULL; + delete penv; + options.env = NULL; +} + +bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) { + leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); + if (!status.ok()) { + printf("LevelDB write failure: %s\n", status.ToString().c_str()); + return false; + } + return true; +} + diff --git a/src/leveldb.h b/src/leveldb.h new file mode 100644 index 0000000000..ee9079c3c3 --- /dev/null +++ b/src/leveldb.h @@ -0,0 +1,144 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_LEVELDB_H +#define BITCOIN_LEVELDB_H + +#include "serialize.h" + +#include <leveldb/db.h> +#include <leveldb/write_batch.h> + +#include <boost/filesystem/path.hpp> + +// Batch of changes queued to be written to a CLevelDB +class CLevelDBBatch +{ + friend class CLevelDB; + +private: + leveldb::WriteBatch batch; + +public: + template<typename K, typename V> void Write(const K& key, const V& value) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(ssValue.GetSerializeSize(value)); + ssValue << value; + leveldb::Slice slValue(&ssValue[0], ssValue.size()); + + batch.Put(slKey, slValue); + } + + template<typename K> void Erase(const K& key) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + batch.Delete(slKey); + } +}; + +class CLevelDB +{ +private: + // custom environment this database is using (may be NULL in case of default environment) + leveldb::Env *penv; + + // database options used + leveldb::Options options; + + // options used when reading from the database + leveldb::ReadOptions readoptions; + + // options used when iterating over values of the database + leveldb::ReadOptions iteroptions; + + // options used when writing to the database + leveldb::WriteOptions writeoptions; + + // options used when sync writing to the database + leveldb::WriteOptions syncoptions; + + // the database itself + leveldb::DB *pdb; + +public: + CLevelDB(const boost::filesystem::path &path, bool fMemory = false); + ~CLevelDB(); + + template<typename K, typename V> bool Read(const K& key, V& value) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + printf("LevelDB read failure: %s\n", status.ToString().c_str()); + } + try { + CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); + ssValue >> value; + } catch(std::exception &e) { + return false; + } + return true; + } + + template<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) { + CLevelDBBatch batch; + batch.Write(key, value); + return WriteBatch(batch, fSync); + } + + template<typename K> bool Exists(const K& key) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + printf("LevelDB read failure: %s\n", status.ToString().c_str()); + } + return true; + } + + template<typename K> bool Erase(const K& key, bool fSync = false) { + CLevelDBBatch batch; + batch.Erase(key); + return WriteBatch(batch, fSync); + } + + bool WriteBatch(CLevelDBBatch &batch, bool fSync = false); + + // not available for LevelDB; provide for compatibility with BDB + bool Flush() { + return true; + } + + bool Sync() { + CLevelDBBatch batch; + return WriteBatch(batch, true); + } + + // not exactly clean encapsulation, but it's easiest for now + leveldb::Iterator *NewIterator() { + return pdb->NewIterator(iteroptions); + } +}; + +#endif // BITCOIN_LEVELDB_H +
\ No newline at end of file diff --git a/src/leveldb/.gitignore b/src/leveldb/.gitignore new file mode 100644 index 0000000000..f030430565 --- /dev/null +++ b/src/leveldb/.gitignore @@ -0,0 +1,8 @@ +build_config.mk +*.a +*.o +*.dylib* +*.so +*.so.* +*_test +db_bench diff --git a/src/leveldb/AUTHORS b/src/leveldb/AUTHORS new file mode 100644 index 0000000000..27a9407e52 --- /dev/null +++ b/src/leveldb/AUTHORS @@ -0,0 +1,8 @@ +# Names should be added to this file like so: +# Name or Organization <email address> + +Google Inc. + +# Initial version authors: +Jeffrey Dean <jeff@google.com> +Sanjay Ghemawat <sanjay@google.com> diff --git a/src/leveldb/LICENSE b/src/leveldb/LICENSE new file mode 100644 index 0000000000..8e80208cd7 --- /dev/null +++ b/src/leveldb/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The LevelDB Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile new file mode 100755 index 0000000000..7f658cfdf9 --- /dev/null +++ b/src/leveldb/Makefile @@ -0,0 +1,200 @@ +# Copyright (c) 2011 The LevelDB Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. See the AUTHORS file for names of contributors. + +# Inherit some settings from environment variables, if available +INSTALL_PATH ?= $(CURDIR) + +#----------------------------------------------- +# Uncomment exactly one of the lines labelled (A), (B), and (C) below +# to switch between compilation modes. + +OPT ?= -O2 -DNDEBUG # (A) Production use (optimized mode) +# OPT ?= -g2 # (B) Debug mode, w/ full line-level debugging symbols +# OPT ?= -O2 -g2 -DNDEBUG # (C) Profiling mode: opt, but w/debugging symbols +#----------------------------------------------- + +# detect what platform we're building on +$(shell ./build_detect_platform build_config.mk) +# this file is generated by the previous line to set build flags and sources +include build_config.mk + +CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) +CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) + +LDFLAGS += $(PLATFORM_LDFLAGS) + +LIBOBJECTS = $(SOURCES:.cc=.o) +MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o) + +TESTUTIL = ./util/testutil.o +TESTHARNESS = ./util/testharness.o $(TESTUTIL) + +TESTS = \ + arena_test \ + bloom_test \ + c_test \ + cache_test \ + coding_test \ + corruption_test \ + crc32c_test \ + db_test \ + dbformat_test \ + env_test \ + filename_test \ + filter_block_test \ + log_test \ + memenv_test \ + skiplist_test \ + table_test \ + version_edit_test \ + version_set_test \ + write_batch_test + +PROGRAMS = db_bench $(TESTS) +BENCHMARKS = db_bench_sqlite3 db_bench_tree_db + +LIBRARY = libleveldb.a +MEMENVLIBRARY = libmemenv.a + +default: all + +# Should we build shared libraries? +ifneq ($(PLATFORM_SHARED_EXT),) + +ifneq ($(PLATFORM_SHARED_VERSIONED),true) +SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED2 = $(SHARED1) +SHARED3 = $(SHARED1) +SHARED = $(SHARED1) +else +# Update db.h if you change these. +SHARED_MAJOR = 1 +SHARED_MINOR = 5 +SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED2 = $(SHARED1).$(SHARED_MAJOR) +SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) +SHARED = $(SHARED1) $(SHARED2) $(SHARED3) +$(SHARED1): $(SHARED3) + ln -fs $(SHARED3) $(SHARED1) +$(SHARED2): $(SHARED3) + ln -fs $(SHARED3) $(SHARED2) +endif + +$(SHARED3): + $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) $(PLATFORM_EXTRALIBS) -o $(SHARED3) + +endif # PLATFORM_SHARED_EXT + +all: $(SHARED) $(LIBRARY) + +check: all $(PROGRAMS) $(TESTS) + for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done + +clean: + -rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk + -rm -rf ios-x86/* ios-arm/* + +$(LIBRARY): $(LIBOBJECTS) + rm -f $@ + $(AR) -rs $@ $(LIBOBJECTS) + +db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) -lsqlite3 + +db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) -lkyotocabinet + +arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +$(MEMENVLIBRARY) : $(MEMENVOBJECTS) + rm -f $@ + $(AR) -rs $@ $(MEMENVOBJECTS) + +memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) + $(CXX) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LDFLAGS) $(PLATFORM_EXTRALIBS) + +ifeq ($(PLATFORM), IOS) +# For iOS, create universal object files to be used on both the simulator and +# a device. +PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms +SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer +DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer +IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString) + +.cc.o: + mkdir -p ios-x86/$(dir $@) + $(SIMULATORROOT)/usr/bin/$(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + mkdir -p ios-arm/$(dir $@) + $(DEVICEROOT)/usr/bin/$(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + lipo ios-x86/$@ ios-arm/$@ -create -output $@ + +.c.o: + mkdir -p ios-x86/$(dir $@) + $(SIMULATORROOT)/usr/bin/$(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + mkdir -p ios-arm/$(dir $@) + $(DEVICEROOT)/usr/bin/$(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + lipo ios-x86/$@ ios-arm/$@ -create -output $@ + +else +.cc.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ +endif diff --git a/src/leveldb/README b/src/leveldb/README new file mode 100644 index 0000000000..2bf787ef23 --- /dev/null +++ b/src/leveldb/README @@ -0,0 +1,59 @@ +LevelDB is a third party library used for the transaction database. +It is imported into the Bitcoin codebase due to being relatively new +and not widely packaged. + + + +--------------------------------------------------------------------- + +leveldb: A key-value store +Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) + +The code under this directory implements a system for maintaining a +persistent key/value store. + +See doc/index.html for more explanation. +See doc/impl.html for a brief overview of the implementation. + +The public interface is in include/*.h. Callers should not include or +rely on the details of any other header files in this package. Those +internal APIs may be changed without warning. + +Guide to header files: + +include/db.h + Main interface to the DB: Start here + +include/options.h + Control over the behavior of an entire database, and also + control over the behavior of individual reads and writes. + +include/comparator.h + Abstraction for user-specified comparison function. If you want + just bytewise comparison of keys, you can use the default comparator, + but clients can write their own comparator implementations if they + want custom ordering (e.g. to handle different character + encodings, etc.) + +include/iterator.h + Interface for iterating over data. You can get an iterator + from a DB object. + +include/write_batch.h + Interface for atomically applying multiple updates to a database. + +include/slice.h + A simple module for maintaining a pointer and a length into some + other byte array. + +include/status.h + Status is returned from many of the public interfaces and is used + to report success and various kinds of errors. + +include/env.h + Abstraction of the OS environment. A posix implementation of + this interface is in util/env_posix.cc + +include/table.h +include/table_builder.h + Lower-level modules that most clients probably won't use directly diff --git a/src/leveldb/TODO b/src/leveldb/TODO new file mode 100644 index 0000000000..9130b6a9fa --- /dev/null +++ b/src/leveldb/TODO @@ -0,0 +1,13 @@ +ss +- Stats + +db +- Maybe implement DB::BulkDeleteForRange(start_key, end_key) + that would blow away files whose ranges are entirely contained + within [start_key..end_key]? For Chrome, deletion of obsolete + object stores, etc. can be done in the background anyway, so + probably not that important. + +After a range is completely deleted, what gets rid of the +corresponding files if we do no future changes to that range. Make +the conditions for triggering compactions fire in more situations? diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform new file mode 100755 index 0000000000..dd982236fd --- /dev/null +++ b/src/leveldb/build_detect_platform @@ -0,0 +1,181 @@ +#!/bin/sh +# +# Detects OS we're compiling on and outputs a file specified by the first +# argument, which in turn gets read while processing Makefile. +# +# The output will set the following variables: +# CC C Compiler path +# CXX C++ Compiler path +# PLATFORM_LDFLAGS Linker flags +# PLATFORM_SHARED_EXT Extension for shared libraries +# PLATFORM_SHARED_LDFLAGS Flags for building shared library +# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library +# PLATFORM_CCFLAGS C compiler flags +# PLATFORM_CXXFLAGS C++ compiler flags. Will contain: +# PLATFORM_SHARED_VERSIONED Set to 'true' if platform supports versioned +# shared libraries, empty otherwise. +# +# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following: +# +# -DLEVELDB_CSTDATOMIC_PRESENT if <cstdatomic> is present +# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms +# -DSNAPPY if the Snappy library is present +# + +OUTPUT=$1 +if test -z "$OUTPUT"; then + echo "usage: $0 <output-filename>" >&2 + exit 1 +fi + +# Delete existing output, if it exists +rm -f $OUTPUT +touch $OUTPUT + +if test -z "$CC"; then + CC=cc +fi + +if test -z "$CXX"; then + CXX=g++ +fi + +# Detect OS +if test -z "$TARGET_OS"; then + TARGET_OS=`uname -s` +fi + +COMMON_FLAGS= +CROSS_COMPILE= +PLATFORM_CCFLAGS= +PLATFORM_CXXFLAGS= +PLATFORM_LDFLAGS= +PLATFORM_EXTRALIBS= +PLATFORM_SOURCES= +PLATFORM_SHARED_EXT="so" +PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," +PLATFORM_SHARED_CFLAGS="-fPIC" +PLATFORM_SHARED_VERSIONED=true + +# On GCC, we pick libc's memcmp over GCC's memcmp via -fno-builtin-memcmp +case "$TARGET_OS" in + Darwin) + PLATFORM=OS_MACOSX + COMMON_FLAGS="-fno-builtin-memcmp -DOS_MACOSX" + PLATFORM_SHARED_EXT=dylib + PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name " + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" + ;; + Linux) + PLATFORM=OS_LINUX + COMMON_FLAGS="-fno-builtin-memcmp -pthread -DOS_LINUX" + PLATFORM_LDFLAGS="-pthread" + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" + ;; + SunOS) + PLATFORM=OS_SOLARIS + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_SOLARIS" + PLATFORM_LDFLAGS="-lpthread -lrt" + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" + ;; + FreeBSD) + PLATFORM=OS_FREEBSD + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_FREEBSD" + PLATFORM_LDFLAGS="-lpthread" + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" + ;; + NetBSD) + PLATFORM=OS_NETBSD + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_NETBSD" + PLATFORM_LDFLAGS="-lpthread -lgcc_s" + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" + ;; + OpenBSD) + PLATFORM=OS_OPENBSD + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_OPENBSD" + PLATFORM_LDFLAGS="-pthread" + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" + ;; + DragonFly) + PLATFORM=OS_DRAGONFLYBSD + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_DRAGONFLYBSD" + PLATFORM_LDFLAGS="-lpthread" + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" + ;; + OS_ANDROID_CROSSCOMPILE) + PLATFORM=OS_ANDROID + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX" + PLATFORM_LDFLAGS="" # All pthread features are in the Android C library + PLATFORM_SOURCES="port/port_posix.cc util/env_posix.cc" + CROSS_COMPILE=true + ;; + OS_WINDOWS_CROSSCOMPILE) + PLATFORM=OS_WINDOWS + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DBOOST_THREAD_USE_LIB" + PLATFORM_CXXFLAGS="" + PLATFORM_LDFLAGS="" + PLATFORM_SHARED_CFLAGS="" + PLATFORM_SOURCES="port/port_win.cc util/env_boost.cc util/win_logger.cc" + PLATFORM_EXTRALIBS="-lboost_system-mt-s -lboost_filesystem-mt-s -lboost_thread_win32-mt-s" + CROSS_COMPILE=true + ;; + *) + echo "Unknown platform!" >&2 + exit 1 +esac + +# We want to make a list of all cc files within util, db, table, and helpers +# except for the test and benchmark files. By default, find will output a list +# of all files matching either rule, so we need to append -print to make the +# prune take effect. +DIRS="util db table" +set -f # temporarily disable globbing so that our patterns aren't expanded +PRUNE_TEST="-name *test*.cc -prune" +PRUNE_BENCH="-name *_bench.cc -prune" +PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o -name '*.cc' -not -name 'env_*.cc' -not -name '*_logger.cc' -print | sort | tr "\n" " "` +set +f # re-enable globbing + +# The sources consist of the portable files, plus the platform-specific port +# file. +echo "SOURCES=$PORTABLE_FILES $PLATFORM_SOURCES" >> $OUTPUT +echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT + +if [ "$CROSS_COMPILE" = "true" ]; then + # Cross-compiling; do not try any compilation tests. + true +else + # If -std=c++0x works, use <cstdatomic>. Otherwise use port_posix.h. + $CXX $CFLAGS -std=c++0x -x c++ - -o /dev/null 2>/dev/null <<EOF + #include <cstdatomic> + int main() {} +EOF + if [ "$?" = 0 ]; then + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_CSTDATOMIC_PRESENT" + PLATFORM_CXXFLAGS="-std=c++0x" + else + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX" + fi + + # Test whether tcmalloc is available + $CXX $CFLAGS -x c++ - -o /dev/null -ltcmalloc 2>/dev/null <<EOF + int main() {} +EOF + if [ "$?" = 0 ]; then + PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltcmalloc" + fi +fi + +PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS" +PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS" + +echo "CC=$CC" >> $OUTPUT +echo "CXX=$CXX" >> $OUTPUT +echo "PLATFORM=$PLATFORM" >> $OUTPUT +echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT +echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT +echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT +echo "PLATFORM_EXTRALIBS=$PLATFORM_EXTRALIBS" >> $OUTPUT +echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT +echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT diff --git a/src/leveldb/db/builder.cc b/src/leveldb/db/builder.cc new file mode 100644 index 0000000000..f419882197 --- /dev/null +++ b/src/leveldb/db/builder.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/builder.h" + +#include "db/filename.h" +#include "db/dbformat.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" + +namespace leveldb { + +Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta) { + Status s; + meta->file_size = 0; + iter->SeekToFirst(); + + std::string fname = TableFileName(dbname, meta->number); + if (iter->Valid()) { + WritableFile* file; + s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + + TableBuilder* builder = new TableBuilder(options, file); + meta->smallest.DecodeFrom(iter->key()); + for (; iter->Valid(); iter->Next()) { + Slice key = iter->key(); + meta->largest.DecodeFrom(key); + builder->Add(key, iter->value()); + } + + // Finish and check for builder errors + if (s.ok()) { + s = builder->Finish(); + if (s.ok()) { + meta->file_size = builder->FileSize(); + assert(meta->file_size > 0); + } + } else { + builder->Abandon(); + } + delete builder; + + // Finish and check for file errors + if (s.ok()) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (s.ok()) { + // Verify that the table is usable + Iterator* it = table_cache->NewIterator(ReadOptions(), + meta->number, + meta->file_size); + s = it->status(); + delete it; + } + } + + // Check for input iterator errors + if (!iter->status().ok()) { + s = iter->status(); + } + + if (s.ok() && meta->file_size > 0) { + // Keep it + } else { + env->DeleteFile(fname); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb/db/builder.h b/src/leveldb/db/builder.h new file mode 100644 index 0000000000..62431fcf44 --- /dev/null +++ b/src/leveldb/db/builder.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_BUILDER_H_ +#define STORAGE_LEVELDB_DB_BUILDER_H_ + +#include "leveldb/status.h" + +namespace leveldb { + +struct Options; +struct FileMetaData; + +class Env; +class Iterator; +class TableCache; +class VersionEdit; + +// Build a Table file from the contents of *iter. The generated file +// will be named according to meta->number. On success, the rest of +// *meta will be filled with metadata about the generated table. +// If no data is present in *iter, meta->file_size will be set to +// zero, and no Table file will be produced. +extern Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_BUILDER_H_ diff --git a/src/leveldb/db/c.cc b/src/leveldb/db/c.cc new file mode 100644 index 0000000000..2dde400e77 --- /dev/null +++ b/src/leveldb/db/c.cc @@ -0,0 +1,581 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/c.h" + +#include <stdlib.h> +#include <unistd.h> +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/write_batch.h" + +using leveldb::Cache; +using leveldb::Comparator; +using leveldb::CompressionType; +using leveldb::DB; +using leveldb::Env; +using leveldb::FileLock; +using leveldb::FilterPolicy; +using leveldb::Iterator; +using leveldb::Logger; +using leveldb::NewBloomFilterPolicy; +using leveldb::NewLRUCache; +using leveldb::Options; +using leveldb::RandomAccessFile; +using leveldb::Range; +using leveldb::ReadOptions; +using leveldb::SequentialFile; +using leveldb::Slice; +using leveldb::Snapshot; +using leveldb::Status; +using leveldb::WritableFile; +using leveldb::WriteBatch; +using leveldb::WriteOptions; + +extern "C" { + +struct leveldb_t { DB* rep; }; +struct leveldb_iterator_t { Iterator* rep; }; +struct leveldb_writebatch_t { WriteBatch rep; }; +struct leveldb_snapshot_t { const Snapshot* rep; }; +struct leveldb_readoptions_t { ReadOptions rep; }; +struct leveldb_writeoptions_t { WriteOptions rep; }; +struct leveldb_options_t { Options rep; }; +struct leveldb_cache_t { Cache* rep; }; +struct leveldb_seqfile_t { SequentialFile* rep; }; +struct leveldb_randomfile_t { RandomAccessFile* rep; }; +struct leveldb_writablefile_t { WritableFile* rep; }; +struct leveldb_logger_t { Logger* rep; }; +struct leveldb_filelock_t { FileLock* rep; }; + +struct leveldb_comparator_t : public Comparator { + void* state_; + void (*destructor_)(void*); + int (*compare_)( + void*, + const char* a, size_t alen, + const char* b, size_t blen); + const char* (*name_)(void*); + + virtual ~leveldb_comparator_t() { + (*destructor_)(state_); + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + // No-ops since the C binding does not support key shortening methods. + virtual void FindShortestSeparator(std::string*, const Slice&) const { } + virtual void FindShortSuccessor(std::string* key) const { } +}; + +struct leveldb_filterpolicy_t : public FilterPolicy { + void* state_; + void (*destructor_)(void*); + const char* (*name_)(void*); + char* (*create_)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length); + unsigned char (*key_match_)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length); + + virtual ~leveldb_filterpolicy_t() { + (*destructor_)(state_); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + std::vector<const char*> key_pointers(n); + std::vector<size_t> key_sizes(n); + for (int i = 0; i < n; i++) { + key_pointers[i] = keys[i].data(); + key_sizes[i] = keys[i].size(); + } + size_t len; + char* filter = (*create_)(state_, &key_pointers[0], &key_sizes[0], n, &len); + dst->append(filter, len); + free(filter); + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return (*key_match_)(state_, key.data(), key.size(), + filter.data(), filter.size()); + } +}; + +struct leveldb_env_t { + Env* rep; + bool is_default; +}; + +static bool SaveError(char** errptr, const Status& s) { + assert(errptr != NULL); + if (s.ok()) { + return false; + } else if (*errptr == NULL) { + *errptr = strdup(s.ToString().c_str()); + } else { + // TODO(sanjay): Merge with existing error? + free(*errptr); + *errptr = strdup(s.ToString().c_str()); + } + return true; +} + +static char* CopyString(const std::string& str) { + char* result = reinterpret_cast<char*>(malloc(sizeof(char) * str.size())); + memcpy(result, str.data(), sizeof(char) * str.size()); + return result; +} + +leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr) { + DB* db; + if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { + return NULL; + } + leveldb_t* result = new leveldb_t; + result->rep = db; + return result; +} + +void leveldb_close(leveldb_t* db) { + delete db->rep; + delete db; +} + +void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr) { + SaveError(errptr, + db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen))); +} + +void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr) { + SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen))); +} + + +void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr) { + SaveError(errptr, db->rep->Write(options->rep, &batch->rep)); +} + +char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr) { + char* result = NULL; + std::string tmp; + Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); + if (s.ok()) { + *vallen = tmp.size(); + result = CopyString(tmp); + } else { + *vallen = 0; + if (!s.IsNotFound()) { + SaveError(errptr, s); + } + } + return result; +} + +leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options) { + leveldb_iterator_t* result = new leveldb_iterator_t; + result->rep = db->rep->NewIterator(options->rep); + return result; +} + +const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db) { + leveldb_snapshot_t* result = new leveldb_snapshot_t; + result->rep = db->rep->GetSnapshot(); + return result; +} + +void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot) { + db->rep->ReleaseSnapshot(snapshot->rep); + delete snapshot; +} + +char* leveldb_property_value( + leveldb_t* db, + const char* propname) { + std::string tmp; + if (db->rep->GetProperty(Slice(propname), &tmp)) { + // We use strdup() since we expect human readable output. + return strdup(tmp.c_str()); + } else { + return NULL; + } +} + +void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes) { + Range* ranges = new Range[num_ranges]; + for (int i = 0; i < num_ranges; i++) { + ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]); + ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]); + } + db->rep->GetApproximateSizes(ranges, num_ranges, sizes); + delete[] ranges; +} + +void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len) { + Slice a, b; + db->rep->CompactRange( + // Pass NULL Slice if corresponding "const char*" is NULL + (start_key ? (a = Slice(start_key, start_key_len), &a) : NULL), + (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL)); +} + +void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, DestroyDB(name, options->rep)); +} + +void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, RepairDB(name, options->rep)); +} + +void leveldb_iter_destroy(leveldb_iterator_t* iter) { + delete iter->rep; + delete iter; +} + +unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) { + return iter->rep->Valid(); +} + +void leveldb_iter_seek_to_first(leveldb_iterator_t* iter) { + iter->rep->SeekToFirst(); +} + +void leveldb_iter_seek_to_last(leveldb_iterator_t* iter) { + iter->rep->SeekToLast(); +} + +void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) { + iter->rep->Seek(Slice(k, klen)); +} + +void leveldb_iter_next(leveldb_iterator_t* iter) { + iter->rep->Next(); +} + +void leveldb_iter_prev(leveldb_iterator_t* iter) { + iter->rep->Prev(); +} + +const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) { + Slice s = iter->rep->key(); + *klen = s.size(); + return s.data(); +} + +const char* leveldb_iter_value(const leveldb_iterator_t* iter, size_t* vlen) { + Slice s = iter->rep->value(); + *vlen = s.size(); + return s.data(); +} + +void leveldb_iter_get_error(const leveldb_iterator_t* iter, char** errptr) { + SaveError(errptr, iter->rep->status()); +} + +leveldb_writebatch_t* leveldb_writebatch_create() { + return new leveldb_writebatch_t; +} + +void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { + delete b; +} + +void leveldb_writebatch_clear(leveldb_writebatch_t* b) { + b->rep.Clear(); +} + +void leveldb_writebatch_put( + leveldb_writebatch_t* b, + const char* key, size_t klen, + const char* val, size_t vlen) { + b->rep.Put(Slice(key, klen), Slice(val, vlen)); +} + +void leveldb_writebatch_delete( + leveldb_writebatch_t* b, + const char* key, size_t klen) { + b->rep.Delete(Slice(key, klen)); +} + +void leveldb_writebatch_iterate( + leveldb_writebatch_t* b, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)) { + class H : public WriteBatch::Handler { + public: + void* state_; + void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen); + void (*deleted_)(void*, const char* k, size_t klen); + virtual void Put(const Slice& key, const Slice& value) { + (*put_)(state_, key.data(), key.size(), value.data(), value.size()); + } + virtual void Delete(const Slice& key) { + (*deleted_)(state_, key.data(), key.size()); + } + }; + H handler; + handler.state_ = state; + handler.put_ = put; + handler.deleted_ = deleted; + b->rep.Iterate(&handler); +} + +leveldb_options_t* leveldb_options_create() { + return new leveldb_options_t; +} + +void leveldb_options_destroy(leveldb_options_t* options) { + delete options; +} + +void leveldb_options_set_comparator( + leveldb_options_t* opt, + leveldb_comparator_t* cmp) { + opt->rep.comparator = cmp; +} + +void leveldb_options_set_filter_policy( + leveldb_options_t* opt, + leveldb_filterpolicy_t* policy) { + opt->rep.filter_policy = policy; +} + +void leveldb_options_set_create_if_missing( + leveldb_options_t* opt, unsigned char v) { + opt->rep.create_if_missing = v; +} + +void leveldb_options_set_error_if_exists( + leveldb_options_t* opt, unsigned char v) { + opt->rep.error_if_exists = v; +} + +void leveldb_options_set_paranoid_checks( + leveldb_options_t* opt, unsigned char v) { + opt->rep.paranoid_checks = v; +} + +void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) { + opt->rep.env = (env ? env->rep : NULL); +} + +void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) { + opt->rep.info_log = (l ? l->rep : NULL); +} + +void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) { + opt->rep.write_buffer_size = s; +} + +void leveldb_options_set_max_open_files(leveldb_options_t* opt, int n) { + opt->rep.max_open_files = n; +} + +void leveldb_options_set_cache(leveldb_options_t* opt, leveldb_cache_t* c) { + opt->rep.block_cache = c->rep; +} + +void leveldb_options_set_block_size(leveldb_options_t* opt, size_t s) { + opt->rep.block_size = s; +} + +void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) { + opt->rep.block_restart_interval = n; +} + +void leveldb_options_set_compression(leveldb_options_t* opt, int t) { + opt->rep.compression = static_cast<CompressionType>(t); +} + +leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)) { + leveldb_comparator_t* result = new leveldb_comparator_t; + result->state_ = state; + result->destructor_ = destructor; + result->compare_ = compare; + result->name_ = name; + return result; +} + +void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { + delete cmp; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)) { + leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t; + result->state_ = state; + result->destructor_ = destructor; + result->create_ = create_filter; + result->key_match_ = key_may_match; + result->name_ = name; + return result; +} + +void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t* filter) { + delete filter; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { + // Make a leveldb_filterpolicy_t, but override all of its methods so + // they delegate to a NewBloomFilterPolicy() instead of user + // supplied C functions. + struct Wrapper : public leveldb_filterpolicy_t { + const FilterPolicy* rep_; + ~Wrapper() { delete rep_; } + const char* Name() const { return rep_->Name(); } + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + return rep_->CreateFilter(keys, n, dst); + } + bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return rep_->KeyMayMatch(key, filter); + } + static void DoNothing(void*) { } + }; + Wrapper* wrapper = new Wrapper; + wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); + wrapper->state_ = NULL; + wrapper->destructor_ = &Wrapper::DoNothing; + return wrapper; +} + +leveldb_readoptions_t* leveldb_readoptions_create() { + return new leveldb_readoptions_t; +} + +void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { + delete opt; +} + +void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t* opt, + unsigned char v) { + opt->rep.verify_checksums = v; +} + +void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t* opt, unsigned char v) { + opt->rep.fill_cache = v; +} + +void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t* opt, + const leveldb_snapshot_t* snap) { + opt->rep.snapshot = (snap ? snap->rep : NULL); +} + +leveldb_writeoptions_t* leveldb_writeoptions_create() { + return new leveldb_writeoptions_t; +} + +void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { + delete opt; +} + +void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t* opt, unsigned char v) { + opt->rep.sync = v; +} + +leveldb_cache_t* leveldb_cache_create_lru(size_t capacity) { + leveldb_cache_t* c = new leveldb_cache_t; + c->rep = NewLRUCache(capacity); + return c; +} + +void leveldb_cache_destroy(leveldb_cache_t* cache) { + delete cache->rep; + delete cache; +} + +leveldb_env_t* leveldb_create_default_env() { + leveldb_env_t* result = new leveldb_env_t; + result->rep = Env::Default(); + result->is_default = true; + return result; +} + +void leveldb_env_destroy(leveldb_env_t* env) { + if (!env->is_default) delete env->rep; + delete env; +} + +} // end extern "C" diff --git a/src/leveldb/db/c_test.c b/src/leveldb/db/c_test.c new file mode 100644 index 0000000000..9792447157 --- /dev/null +++ b/src/leveldb/db/c_test.c @@ -0,0 +1,381 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. */ + +#include "leveldb/c.h" + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +const char* phase = ""; +static char dbname[200]; + +static void StartPhase(const char* name) { + fprintf(stderr, "=== Test %s\n", name); + phase = name; +} + +static const char* GetTempDir(void) { + const char* ret = getenv("TEST_TMPDIR"); + if (ret == NULL || ret[0] == '\0') + ret = "/tmp"; + return ret; +} + +#define CheckNoError(err) \ + if ((err) != NULL) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \ + abort(); \ + } + +#define CheckCondition(cond) \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \ + abort(); \ + } + +static void CheckEqual(const char* expected, const char* v, size_t n) { + if (expected == NULL && v == NULL) { + // ok + } else if (expected != NULL && v != NULL && n == strlen(expected) && + memcmp(expected, v, n) == 0) { + // ok + return; + } else { + fprintf(stderr, "%s: expected '%s', got '%s'\n", + phase, + (expected ? expected : "(null)"), + (v ? v : "(null")); + abort(); + } +} + +static void Free(char** ptr) { + if (*ptr) { + free(*ptr); + *ptr = NULL; + } +} + +static void CheckGet( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, + const char* expected) { + char* err = NULL; + size_t val_len; + char* val; + val = leveldb_get(db, options, key, strlen(key), &val_len, &err); + CheckNoError(err); + CheckEqual(expected, val, val_len); + Free(&val); +} + +static void CheckIter(leveldb_iterator_t* iter, + const char* key, const char* val) { + size_t len; + const char* str; + str = leveldb_iter_key(iter, &len); + CheckEqual(key, str, len); + str = leveldb_iter_value(iter, &len); + CheckEqual(val, str, len); +} + +// Callback from leveldb_writebatch_iterate() +static void CheckPut(void* ptr, + const char* k, size_t klen, + const char* v, size_t vlen) { + int* state = (int*) ptr; + CheckCondition(*state < 2); + switch (*state) { + case 0: + CheckEqual("bar", k, klen); + CheckEqual("b", v, vlen); + break; + case 1: + CheckEqual("box", k, klen); + CheckEqual("c", v, vlen); + break; + } + (*state)++; +} + +// Callback from leveldb_writebatch_iterate() +static void CheckDel(void* ptr, const char* k, size_t klen) { + int* state = (int*) ptr; + CheckCondition(*state == 2); + CheckEqual("bar", k, klen); + (*state)++; +} + +static void CmpDestroy(void* arg) { } + +static int CmpCompare(void* arg, const char* a, size_t alen, + const char* b, size_t blen) { + int n = (alen < blen) ? alen : blen; + int r = memcmp(a, b, n); + if (r == 0) { + if (alen < blen) r = -1; + else if (alen > blen) r = +1; + } + return r; +} + +static const char* CmpName(void* arg) { + return "foo"; +} + +// Custom filter policy +static unsigned char fake_filter_result = 1; +static void FilterDestroy(void* arg) { } +static const char* FilterName(void* arg) { + return "TestFilter"; +} +static char* FilterCreate( + void* arg, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length) { + *filter_length = 4; + char* result = malloc(4); + memcpy(result, "fake", 4); + return result; +} +unsigned char FilterKeyMatch( + void* arg, + const char* key, size_t length, + const char* filter, size_t filter_length) { + CheckCondition(filter_length == 4); + CheckCondition(memcmp(filter, "fake", 4) == 0); + return fake_filter_result; +} + +int main(int argc, char** argv) { + leveldb_t* db; + leveldb_comparator_t* cmp; + leveldb_cache_t* cache; + leveldb_env_t* env; + leveldb_options_t* options; + leveldb_readoptions_t* roptions; + leveldb_writeoptions_t* woptions; + char* err = NULL; + int run = -1; + + snprintf(dbname, sizeof(dbname), + "%s/leveldb_c_test-%d", + GetTempDir(), + ((int) geteuid())); + + StartPhase("create_objects"); + cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); + env = leveldb_create_default_env(); + cache = leveldb_cache_create_lru(100000); + + options = leveldb_options_create(); + leveldb_options_set_comparator(options, cmp); + leveldb_options_set_error_if_exists(options, 1); + leveldb_options_set_cache(options, cache); + leveldb_options_set_env(options, env); + leveldb_options_set_info_log(options, NULL); + leveldb_options_set_write_buffer_size(options, 100000); + leveldb_options_set_paranoid_checks(options, 1); + leveldb_options_set_max_open_files(options, 10); + leveldb_options_set_block_size(options, 1024); + leveldb_options_set_block_restart_interval(options, 8); + leveldb_options_set_compression(options, leveldb_no_compression); + + roptions = leveldb_readoptions_create(); + leveldb_readoptions_set_verify_checksums(roptions, 1); + leveldb_readoptions_set_fill_cache(roptions, 0); + + woptions = leveldb_writeoptions_create(); + leveldb_writeoptions_set_sync(woptions, 1); + + StartPhase("destroy"); + leveldb_destroy_db(options, dbname, &err); + Free(&err); + + StartPhase("open_error"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + Free(&err); + + StartPhase("open"); + leveldb_options_set_create_if_missing(options, 1); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + + StartPhase("put"); + leveldb_put(db, woptions, "foo", 3, "hello", 5, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactall"); + leveldb_compact_range(db, NULL, 0, NULL, 0); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactrange"); + leveldb_compact_range(db, "a", 1, "z", 1); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("writebatch"); + { + leveldb_writebatch_t* wb = leveldb_writebatch_create(); + leveldb_writebatch_put(wb, "foo", 3, "a", 1); + leveldb_writebatch_clear(wb); + leveldb_writebatch_put(wb, "bar", 3, "b", 1); + leveldb_writebatch_put(wb, "box", 3, "c", 1); + leveldb_writebatch_delete(wb, "bar", 3); + leveldb_write(db, woptions, wb, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + int pos = 0; + leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); + CheckCondition(pos == 3); + leveldb_writebatch_destroy(wb); + } + + StartPhase("iter"); + { + leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_first(iter); + CheckCondition(leveldb_iter_valid(iter)); + CheckIter(iter, "box", "c"); + leveldb_iter_next(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_prev(iter); + CheckIter(iter, "box", "c"); + leveldb_iter_prev(iter); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_last(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_seek(iter, "b", 1); + CheckIter(iter, "box", "c"); + leveldb_iter_get_error(iter, &err); + CheckNoError(err); + leveldb_iter_destroy(iter); + } + + StartPhase("approximate_sizes"); + { + int i; + int n = 20000; + char keybuf[100]; + char valbuf[100]; + uint64_t sizes[2]; + const char* start[2] = { "a", "k00000000000000010000" }; + size_t start_len[2] = { 1, 21 }; + const char* limit[2] = { "k00000000000000010000", "z" }; + size_t limit_len[2] = { 21, 1 }; + leveldb_writeoptions_set_sync(woptions, 0); + for (i = 0; i < n; i++) { + snprintf(keybuf, sizeof(keybuf), "k%020d", i); + snprintf(valbuf, sizeof(valbuf), "v%020d", i); + leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf), + &err); + CheckNoError(err); + } + leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes); + CheckCondition(sizes[0] > 0); + CheckCondition(sizes[1] > 0); + } + + StartPhase("property"); + { + char* prop = leveldb_property_value(db, "nosuchprop"); + CheckCondition(prop == NULL); + prop = leveldb_property_value(db, "leveldb.stats"); + CheckCondition(prop != NULL); + Free(&prop); + } + + StartPhase("snapshot"); + { + const leveldb_snapshot_t* snap; + snap = leveldb_create_snapshot(db); + leveldb_delete(db, woptions, "foo", 3, &err); + CheckNoError(err); + leveldb_readoptions_set_snapshot(roptions, snap); + CheckGet(db, roptions, "foo", "hello"); + leveldb_readoptions_set_snapshot(roptions, NULL); + CheckGet(db, roptions, "foo", NULL); + leveldb_release_snapshot(db, snap); + } + + StartPhase("repair"); + { + leveldb_close(db); + leveldb_options_set_create_if_missing(options, 0); + leveldb_options_set_error_if_exists(options, 0); + leveldb_repair_db(options, dbname, &err); + CheckNoError(err); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + leveldb_options_set_create_if_missing(options, 1); + leveldb_options_set_error_if_exists(options, 1); + } + + StartPhase("filter"); + for (run = 0; run < 2; run++) { + // First run uses custom filter, second run uses bloom filter + CheckNoError(err); + leveldb_filterpolicy_t* policy; + if (run == 0) { + policy = leveldb_filterpolicy_create( + NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName); + } else { + policy = leveldb_filterpolicy_create_bloom(10); + } + + // Create new database + leveldb_close(db); + leveldb_destroy_db(options, dbname, &err); + leveldb_options_set_filter_policy(options, policy); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err); + CheckNoError(err); + leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err); + CheckNoError(err); + leveldb_compact_range(db, NULL, 0, NULL, 0); + + fake_filter_result = 1; + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + if (phase == 0) { + // Must not find value when custom filter returns false + fake_filter_result = 0; + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + fake_filter_result = 1; + + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + } + leveldb_options_set_filter_policy(options, NULL); + leveldb_filterpolicy_destroy(policy); + } + + StartPhase("cleanup"); + leveldb_close(db); + leveldb_options_destroy(options); + leveldb_readoptions_destroy(roptions); + leveldb_writeoptions_destroy(woptions); + leveldb_cache_destroy(cache); + leveldb_comparator_destroy(cmp); + leveldb_env_destroy(env); + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc new file mode 100644 index 0000000000..31b2d5f416 --- /dev/null +++ b/src/leveldb/db/corruption_test.cc @@ -0,0 +1,359 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/log_format.h" +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kValueSize = 1000; + +class CorruptionTest { + public: + test::ErrorEnv env_; + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + CorruptionTest() { + tiny_cache_ = NewLRUCache(100); + options_.env = &env_; + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, options_); + + db_ = NULL; + options_.create_if_missing = true; + Reopen(); + options_.create_if_missing = false; + } + + ~CorruptionTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + Status TryReopen(Options* options = NULL) { + delete db_; + db_ = NULL; + Options opt = (options ? *options : options_); + opt.env = &env_; + opt.block_cache = tiny_cache_; + return DB::Open(opt, dbname_, &db_); + } + + void Reopen(Options* options = NULL) { + ASSERT_OK(TryReopen(options)); + } + + void RepairDB() { + delete db_; + db_ = NULL; + ASSERT_OK(::leveldb::RepairDB(dbname_, options_)); + } + + void Build(int n) { + std::string key_space, value_space; + WriteBatch batch; + for (int i = 0; i < n; i++) { + //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); + Slice key = Key(i, &key_space); + batch.Clear(); + batch.Put(key, Value(i, &value_space)); + ASSERT_OK(db_->Write(WriteOptions(), &batch)); + } + } + + void Check(int min_expected, int max_expected) { + int next_expected = 0; + int missed = 0; + int bad_keys = 0; + int bad_values = 0; + int correct = 0; + std::string value_space; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + uint64_t key; + Slice in(iter->key()); + if (!ConsumeDecimalNumber(&in, &key) || + !in.empty() || + key < next_expected) { + bad_keys++; + continue; + } + missed += (key - next_expected); + next_expected = key + 1; + if (iter->value() != Value(key, &value_space)) { + bad_values++; + } else { + correct++; + } + } + delete iter; + + fprintf(stderr, + "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n", + min_expected, max_expected, correct, bad_keys, bad_values, missed); + ASSERT_LE(min_expected, correct); + ASSERT_GE(max_expected, correct); + } + + void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) { + // Pick file to corrupt + std::vector<std::string> filenames; + ASSERT_OK(env_.GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + std::string fname; + int picked_number = -1; + for (int i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type == filetype && + int(number) > picked_number) { // Pick latest file + fname = dbname_ + "/" + filenames[i]; + picked_number = number; + } + } + ASSERT_TRUE(!fname.empty()) << filetype; + + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + const char* msg = strerror(errno); + ASSERT_TRUE(false) << fname << ": " << msg; + } + + if (offset < 0) { + // Relative to end of file; make it absolute + if (-offset > sbuf.st_size) { + offset = 0; + } else { + offset = sbuf.st_size + offset; + } + } + if (offset > sbuf.st_size) { + offset = sbuf.st_size; + } + if (offset + bytes_to_corrupt > sbuf.st_size) { + bytes_to_corrupt = sbuf.st_size - offset; + } + + // Do it + std::string contents; + Status s = ReadFileToString(Env::Default(), fname, &contents); + ASSERT_TRUE(s.ok()) << s.ToString(); + for (int i = 0; i < bytes_to_corrupt; i++) { + contents[i + offset] ^= 0x80; + } + s = WriteStringToFile(Env::Default(), contents, fname); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + int Property(const std::string& name) { + std::string property; + int result; + if (db_->GetProperty(name, &property) && + sscanf(property.c_str(), "%d", &result) == 1) { + return result; + } else { + return -1; + } + } + + // Return the ith key + Slice Key(int i, std::string* storage) { + char buf[100]; + snprintf(buf, sizeof(buf), "%016d", i); + storage->assign(buf, strlen(buf)); + return Slice(*storage); + } + + // Return the value to associate with the specified key + Slice Value(int k, std::string* storage) { + Random r(k); + return test::RandomString(&r, kValueSize, storage); + } +}; + +TEST(CorruptionTest, Recovery) { + Build(100); + Check(100, 100); + Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record + Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block + Reopen(); + + // The 64 records in the first two log blocks are completely lost. + Check(36, 36); +} + +TEST(CorruptionTest, RecoverWriteError) { + env_.writable_file_error_ = true; + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); +} + +TEST(CorruptionTest, NewFileErrorDuringWrite) { + // Do enough writing to force minor compaction + env_.writable_file_error_ = true; + const int num = 3 + (Options().write_buffer_size / kValueSize); + std::string value_storage; + Status s; + for (int i = 0; s.ok() && i < num; i++) { + WriteBatch batch; + batch.Put("a", Value(100, &value_storage)); + s = db_->Write(WriteOptions(), &batch); + } + ASSERT_TRUE(!s.ok()); + ASSERT_GE(env_.num_writable_file_errors_, 1); + env_.writable_file_error_ = false; + Reopen(); +} + +TEST(CorruptionTest, TableFile) { + Build(100); + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + Check(99, 99); +} + +TEST(CorruptionTest, TableFileIndexData) { + Build(10000); // Enough to build multiple Tables + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + dbi->TEST_CompactMemTable(); + + Corrupt(kTableFile, -2000, 500); + Reopen(); + Check(5000, 9999); +} + +TEST(CorruptionTest, MissingDescriptor) { + Build(1000); + RepairDB(); + Reopen(); + Check(1000, 1000); +} + +TEST(CorruptionTest, SequenceNumberRecovery) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5")); + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v5", v); + // Write something. If sequence number was not recovered properly, + // it will be hidden by an earlier write. + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6")); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); + Reopen(); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); +} + +TEST(CorruptionTest, CorruptedDescriptor) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello")); + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + + Corrupt(kDescriptorFile, 0, 1000); + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); + + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("hello", v); +} + +TEST(CorruptionTest, CompactionInputError) { + Build(10); + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + dbi->TEST_CompactMemTable(); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last))); + + Corrupt(kTableFile, 100, 1); + Check(9, 9); + + // Force compactions by writing lots of values + Build(10000); + Check(10000, 10000); +} + +TEST(CorruptionTest, CompactionInputErrorParanoid) { + Options options; + options.paranoid_checks = true; + options.write_buffer_size = 1048576; + Reopen(&options); + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + + // Fill levels >= 1 so memtable compaction outputs to level 1 + for (int level = 1; level < config::kNumLevels; level++) { + dbi->Put(WriteOptions(), "", "begin"); + dbi->Put(WriteOptions(), "~", "end"); + dbi->TEST_CompactMemTable(); + } + + Build(10); + dbi->TEST_CompactMemTable(); + ASSERT_EQ(1, Property("leveldb.num-files-at-level0")); + + Corrupt(kTableFile, 100, 1); + Check(9, 9); + + // Write must eventually fail because of corrupted table + Status s; + std::string tmp1, tmp2; + for (int i = 0; i < 10000 && s.ok(); i++) { + s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2)); + } + ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db"; +} + +TEST(CorruptionTest, UnrelatedKeys) { + Build(10); + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + + std::string tmp1, tmp2; + ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2))); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); + dbi->TEST_CompactMemTable(); + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/db_bench.cc b/src/leveldb/db/db_bench.cc new file mode 100644 index 0000000000..21d3e25f31 --- /dev/null +++ b/src/leveldb/db/db_bench.cc @@ -0,0 +1,978 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include "db/db_impl.h" +#include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "port/port.h" +#include "util/crc32c.h" +#include "util/histogram.h" +#include "util/mutexlock.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillsync -- write N/100 values in random key order in sync mode +// fill100K -- write N/1000 100K values in random order in async mode +// deleteseq -- delete N keys in sequential order +// deleterandom -- delete N keys in random order +// readseq -- read N times sequentially +// readreverse -- read N times in reverse order +// readrandom -- read N times in random order +// readmissing -- read N missing keys in random order +// readhot -- read N times in random order from 1% section of DB +// seekrandom -- N random seeks +// crc32c -- repeated crc32c of 4K of data +// acquireload -- load N*1000 times +// Meta operations: +// compact -- Compact the entire DB +// stats -- Print DB stats +// sstables -- Print sstable info +// heapprofile -- Dump a heap profile (if supported by this port) +static const char* FLAGS_benchmarks = + "fillseq," + "fillsync," + "fillrandom," + "overwrite," + "readrandom," + "readrandom," // Extra run to allow previous compactions to quiesce + "readseq," + "readreverse," + "compact," + "readrandom," + "readseq," + "readreverse," + "fill100K," + "crc32c," + "snappycomp," + "snappyuncomp," + "acquireload," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Number of concurrent threads to run. +static int FLAGS_threads = 1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Number of bytes to buffer in memtable before compacting +// (initialized to default value by "main") +static int FLAGS_write_buffer_size = 0; + +// Number of bytes to use as a cache of uncompressed data. +// Negative means use default settings. +static int FLAGS_cache_size = -1; + +// Maximum number of files to keep open at the same time (use default if == 0) +static int FLAGS_open_files = 0; + +// Bloom filter bits per key. +// Negative means use default settings. +static int FLAGS_bloom_bits = -1; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +namespace leveldb { + +namespace { + +// Helper for quickly generating random data. +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +static void AppendWithSpace(std::string* str, Slice msg) { + if (msg.empty()) return; + if (!str->empty()) { + str->push_back(' '); + } + str->append(msg.data(), msg.size()); +} + +class Stats { + private: + double start_; + double finish_; + double seconds_; + int done_; + int next_report_; + int64_t bytes_; + double last_op_finish_; + Histogram hist_; + std::string message_; + + public: + Stats() { Start(); } + + void Start() { + next_report_ = 100; + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + bytes_ = 0; + seconds_ = 0; + start_ = Env::Default()->NowMicros(); + finish_ = start_; + message_.clear(); + } + + void Merge(const Stats& other) { + hist_.Merge(other.hist_); + done_ += other.done_; + bytes_ += other.bytes_; + seconds_ += other.seconds_; + if (other.start_ < start_) start_ = other.start_; + if (other.finish_ > finish_) finish_ = other.finish_; + + // Just keep the messages from one thread + if (message_.empty()) message_ = other.message_; + } + + void Stop() { + finish_ = Env::Default()->NowMicros(); + seconds_ = (finish_ - start_) * 1e-6; + } + + void AddMessage(Slice msg) { + AppendWithSpace(&message_, msg); + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros(); + double micros = now - last_op_finish_; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void AddBytes(int64_t n) { + bytes_ += n; + } + + void Report(const Slice& name) { + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + std::string extra; + if (bytes_ > 0) { + // Rate is computed on actual elapsed time, not the sum of per-thread + // elapsed times. + double elapsed = (finish_ - start_) * 1e-6; + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / elapsed); + extra = rate; + } + AppendWithSpace(&extra, message_); + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + seconds_ * 1e6 / done_, + (extra.empty() ? "" : " "), + extra.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } +}; + +// State shared by all concurrent executions of the same benchmark. +struct SharedState { + port::Mutex mu; + port::CondVar cv; + int total; + + // Each thread goes through the following states: + // (1) initializing + // (2) waiting for others to be initialized + // (3) running + // (4) done + + int num_initialized; + int num_done; + bool start; + + SharedState() : cv(&mu) { } +}; + +// Per-thread state for concurrent executions of the same benchmark. +struct ThreadState { + int tid; // 0..n-1 when running in n threads + Random rand; // Has different seeds for different threads + Stats stats; + SharedState* shared; + + ThreadState(int index) + : tid(index), + rand(1000 + index) { + } +}; + +} // namespace + +class Benchmark { + private: + Cache* cache_; + const FilterPolicy* filter_policy_; + DB* db_; + int num_; + int value_size_; + int entries_per_batch_; + WriteOptions write_options_; + int reads_; + int heap_counter_; + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + + // See if snappy is working by attempting to compress a compressible string + const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"; + std::string compressed; + if (!port::Snappy_Compress(text, sizeof(text), &compressed)) { + fprintf(stdout, "WARNING: Snappy compression is not enabled\n"); + } else if (compressed.size() >= sizeof(text)) { + fprintf(stdout, "WARNING: Snappy compression is not effective\n"); + } + } + + void PrintEnvironment() { + fprintf(stderr, "LevelDB: version %d.%d\n", + kMajorVersion, kMinorVersion); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + public: + Benchmark() + : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL), + filter_policy_(FLAGS_bloom_bits >= 0 + ? NewBloomFilterPolicy(FLAGS_bloom_bits) + : NULL), + db_(NULL), + num_(FLAGS_num), + value_size_(FLAGS_value_size), + entries_per_batch_(1), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + heap_counter_(0) { + std::vector<std::string> files; + Env::Default()->GetChildren(FLAGS_db, &files); + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("heap-")) { + Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); + } + } + if (!FLAGS_use_existing_db) { + DestroyDB(FLAGS_db, Options()); + } + } + + ~Benchmark() { + delete db_; + delete cache_; + delete filter_policy_; + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + // Reset parameters that may be overriddden bwlow + num_ = FLAGS_num; + reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads); + value_size_ = FLAGS_value_size; + entries_per_batch_ = 1; + write_options_ = WriteOptions(); + + void (Benchmark::*method)(ThreadState*) = NULL; + bool fresh_db = false; + int num_threads = FLAGS_threads; + + if (name == Slice("fillseq")) { + fresh_db = true; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillbatch")) { + fresh_db = true; + entries_per_batch_ = 1000; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillrandom")) { + fresh_db = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("overwrite")) { + fresh_db = false; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fillsync")) { + fresh_db = true; + num_ /= 1000; + write_options_.sync = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fill100K")) { + fresh_db = true; + num_ /= 1000; + value_size_ = 100 * 1000; + method = &Benchmark::WriteRandom; + } else if (name == Slice("readseq")) { + method = &Benchmark::ReadSequential; + } else if (name == Slice("readreverse")) { + method = &Benchmark::ReadReverse; + } else if (name == Slice("readrandom")) { + method = &Benchmark::ReadRandom; + } else if (name == Slice("readmissing")) { + method = &Benchmark::ReadMissing; + } else if (name == Slice("seekrandom")) { + method = &Benchmark::SeekRandom; + } else if (name == Slice("readhot")) { + method = &Benchmark::ReadHot; + } else if (name == Slice("readrandomsmall")) { + reads_ /= 1000; + method = &Benchmark::ReadRandom; + } else if (name == Slice("deleteseq")) { + method = &Benchmark::DeleteSeq; + } else if (name == Slice("deleterandom")) { + method = &Benchmark::DeleteRandom; + } else if (name == Slice("readwhilewriting")) { + num_threads++; // Add extra thread for writing + method = &Benchmark::ReadWhileWriting; + } else if (name == Slice("compact")) { + method = &Benchmark::Compact; + } else if (name == Slice("crc32c")) { + method = &Benchmark::Crc32c; + } else if (name == Slice("acquireload")) { + method = &Benchmark::AcquireLoad; + } else if (name == Slice("snappycomp")) { + method = &Benchmark::SnappyCompress; + } else if (name == Slice("snappyuncomp")) { + method = &Benchmark::SnappyUncompress; + } else if (name == Slice("heapprofile")) { + HeapProfile(); + } else if (name == Slice("stats")) { + PrintStats("leveldb.stats"); + } else if (name == Slice("sstables")) { + PrintStats("leveldb.sstables"); + } else { + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + + if (fresh_db) { + if (FLAGS_use_existing_db) { + fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n", + name.ToString().c_str()); + method = NULL; + } else { + delete db_; + db_ = NULL; + DestroyDB(FLAGS_db, Options()); + Open(); + } + } + + if (method != NULL) { + RunBenchmark(num_threads, name, method); + } + } + } + + private: + struct ThreadArg { + Benchmark* bm; + SharedState* shared; + ThreadState* thread; + void (Benchmark::*method)(ThreadState*); + }; + + static void ThreadBody(void* v) { + ThreadArg* arg = reinterpret_cast<ThreadArg*>(v); + SharedState* shared = arg->shared; + ThreadState* thread = arg->thread; + { + MutexLock l(&shared->mu); + shared->num_initialized++; + if (shared->num_initialized >= shared->total) { + shared->cv.SignalAll(); + } + while (!shared->start) { + shared->cv.Wait(); + } + } + + thread->stats.Start(); + (arg->bm->*(arg->method))(thread); + thread->stats.Stop(); + + { + MutexLock l(&shared->mu); + shared->num_done++; + if (shared->num_done >= shared->total) { + shared->cv.SignalAll(); + } + } + } + + void RunBenchmark(int n, Slice name, + void (Benchmark::*method)(ThreadState*)) { + SharedState shared; + shared.total = n; + shared.num_initialized = 0; + shared.num_done = 0; + shared.start = false; + + ThreadArg* arg = new ThreadArg[n]; + for (int i = 0; i < n; i++) { + arg[i].bm = this; + arg[i].method = method; + arg[i].shared = &shared; + arg[i].thread = new ThreadState(i); + arg[i].thread->shared = &shared; + Env::Default()->StartThread(ThreadBody, &arg[i]); + } + + shared.mu.Lock(); + while (shared.num_initialized < n) { + shared.cv.Wait(); + } + + shared.start = true; + shared.cv.SignalAll(); + while (shared.num_done < n) { + shared.cv.Wait(); + } + shared.mu.Unlock(); + + for (int i = 1; i < n; i++) { + arg[0].thread->stats.Merge(arg[i].thread->stats); + } + arg[0].thread->stats.Report(name); + + for (int i = 0; i < n; i++) { + delete arg[i].thread; + } + delete[] arg; + } + + void Crc32c(ThreadState* thread) { + // Checksum about 500MB of data total + const int size = 4096; + const char* label = "(4K per op)"; + std::string data(size, 'x'); + int64_t bytes = 0; + uint32_t crc = 0; + while (bytes < 500 * 1048576) { + crc = crc32c::Value(data.data(), size); + thread->stats.FinishedSingleOp(); + bytes += size; + } + // Print so result is not dead + fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc)); + + thread->stats.AddBytes(bytes); + thread->stats.AddMessage(label); + } + + void AcquireLoad(ThreadState* thread) { + int dummy; + port::AtomicPointer ap(&dummy); + int count = 0; + void *ptr = NULL; + thread->stats.AddMessage("(each op is 1000 loads)"); + while (count < 100000) { + for (int i = 0; i < 1000; i++) { + ptr = ap.Acquire_Load(); + } + count++; + thread->stats.FinishedSingleOp(); + } + if (ptr == NULL) exit(1); // Disable unused variable warning. + } + + void SnappyCompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + int64_t bytes = 0; + int64_t produced = 0; + bool ok = true; + std::string compressed; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + produced += compressed.size(); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "(output: %.1f%%)", + (produced * 100.0) / bytes); + thread->stats.AddMessage(buf); + thread->stats.AddBytes(bytes); + } + } + + void SnappyUncompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + std::string compressed; + bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + int64_t bytes = 0; + char* uncompressed = new char[input.size()]; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), + uncompressed); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + delete[] uncompressed; + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + thread->stats.AddBytes(bytes); + } + } + + void Open() { + assert(db_ == NULL); + Options options; + options.create_if_missing = !FLAGS_use_existing_db; + options.block_cache = cache_; + options.write_buffer_size = FLAGS_write_buffer_size; + options.filter_policy = filter_policy_; + Status s = DB::Open(options, FLAGS_db, &db_); + if (!s.ok()) { + fprintf(stderr, "open error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + void WriteSeq(ThreadState* thread) { + DoWrite(thread, true); + } + + void WriteRandom(ThreadState* thread) { + DoWrite(thread, false); + } + + void DoWrite(ThreadState* thread, bool seq) { + if (num_ != FLAGS_num) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_); + thread->stats.AddMessage(msg); + } + + RandomGenerator gen; + WriteBatch batch; + Status s; + int64_t bytes = 0; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Put(key, gen.Generate(value_size_)); + bytes += value_size_ + strlen(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + thread->stats.AddBytes(bytes); + } + + void ReadSequential(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadReverse(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + if (db_->Get(options, key, &value).ok()) { + found++; + } + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void ReadMissing(ThreadState* thread) { + ReadOptions options; + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d.", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void ReadHot(ThreadState* thread) { + ReadOptions options; + std::string value; + const int range = (FLAGS_num + 99) / 100; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % range; + snprintf(key, sizeof(key), "%016d", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void SeekRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + Iterator* iter = db_->NewIterator(options); + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + iter->Seek(key); + if (iter->Valid() && iter->key() == key) found++; + delete iter; + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void DoDelete(ThreadState* thread, bool seq) { + RandomGenerator gen; + WriteBatch batch; + Status s; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Delete(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "del error: %s\n", s.ToString().c_str()); + exit(1); + } + } + } + + void DeleteSeq(ThreadState* thread) { + DoDelete(thread, true); + } + + void DeleteRandom(ThreadState* thread) { + DoDelete(thread, false); + } + + void ReadWhileWriting(ThreadState* thread) { + if (thread->tid > 0) { + ReadRandom(thread); + } else { + // Special thread that keeps writing until other threads are done. + RandomGenerator gen; + while (true) { + { + MutexLock l(&thread->shared->mu); + if (thread->shared->num_done + 1 >= thread->shared->num_initialized) { + // Other threads have finished + break; + } + } + + const int k = thread->rand.Next() % FLAGS_num; + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + Status s = db_->Put(write_options_, key, gen.Generate(value_size_)); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + // Do not count any of the preceding work/delay in stats. + thread->stats.Start(); + } + } + + void Compact(ThreadState* thread) { + db_->CompactRange(NULL, NULL); + } + + void PrintStats(const char* key) { + std::string stats; + if (!db_->GetProperty(key, &stats)) { + stats = "(failed)"; + } + fprintf(stdout, "\n%s\n", stats.c_str()); + } + + static void WriteToFile(void* arg, const char* buf, int n) { + reinterpret_cast<WritableFile*>(arg)->Append(Slice(buf, n)); + } + + void HeapProfile() { + char fname[100]; + snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_); + WritableFile* file; + Status s = Env::Default()->NewWritableFile(fname, &file); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + return; + } + bool ok = port::GetHeapProfile(WriteToFile, file); + delete file; + if (!ok) { + fprintf(stderr, "heap profiling not supported\n"); + Env::Default()->DeleteFile(fname); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + FLAGS_write_buffer_size = leveldb::Options().write_buffer_size; + FLAGS_open_files = leveldb::Options().max_open_files; + std::string default_db_path; + + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) { + FLAGS_threads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) { + FLAGS_write_buffer_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) { + FLAGS_bloom_bits = n; + } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) { + FLAGS_open_files = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db=<path> + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc new file mode 100644 index 0000000000..90c1c811d8 --- /dev/null +++ b/src/leveldb/db/db_impl.cc @@ -0,0 +1,1463 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" + +#include <algorithm> +#include <set> +#include <string> +#include <stdint.h> +#include <stdio.h> +#include <vector> +#include "db/builder.h" +#include "db/db_iter.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/table_builder.h" +#include "port/port.h" +#include "table/block.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" +#include "util/mutexlock.h" + +namespace leveldb { + +// Information kept for every waiting writer +struct DBImpl::Writer { + Status status; + WriteBatch* batch; + bool sync; + bool done; + port::CondVar cv; + + explicit Writer(port::Mutex* mu) : cv(mu) { } +}; + +struct DBImpl::CompactionState { + Compaction* const compaction; + + // Sequence numbers < smallest_snapshot are not significant since we + // will never have to service a snapshot below smallest_snapshot. + // Therefore if we have seen a sequence number S <= smallest_snapshot, + // we can drop all entries for the same key with sequence numbers < S. + SequenceNumber smallest_snapshot; + + // Files produced by compaction + struct Output { + uint64_t number; + uint64_t file_size; + InternalKey smallest, largest; + }; + std::vector<Output> outputs; + + // State kept for output being generated + WritableFile* outfile; + TableBuilder* builder; + + uint64_t total_bytes; + + Output* current_output() { return &outputs[outputs.size()-1]; } + + explicit CompactionState(Compaction* c) + : compaction(c), + outfile(NULL), + builder(NULL), + total_bytes(0) { + } +}; + +// Fix user-supplied options to be reasonable +template <class T,class V> +static void ClipToRange(T* ptr, V minvalue, V maxvalue) { + if (static_cast<V>(*ptr) > maxvalue) *ptr = maxvalue; + if (static_cast<V>(*ptr) < minvalue) *ptr = minvalue; +} +Options SanitizeOptions(const std::string& dbname, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src) { + Options result = src; + result.comparator = icmp; + result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; + ClipToRange(&result.max_open_files, 20, 50000); + ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); + ClipToRange(&result.block_size, 1<<10, 4<<20); + if (result.info_log == NULL) { + // Open a log file in the same directory as the db + src.env->CreateDir(dbname); // In case it does not exist + src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname)); + Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log); + if (!s.ok()) { + // No place suitable for logging + result.info_log = NULL; + } + } + if (result.block_cache == NULL) { + result.block_cache = NewLRUCache(8 << 20); + } + return result; +} + +DBImpl::DBImpl(const Options& options, const std::string& dbname) + : env_(options.env), + internal_comparator_(options.comparator), + internal_filter_policy_(options.filter_policy), + options_(SanitizeOptions( + dbname, &internal_comparator_, &internal_filter_policy_, options)), + owns_info_log_(options_.info_log != options.info_log), + owns_cache_(options_.block_cache != options.block_cache), + dbname_(dbname), + db_lock_(NULL), + shutting_down_(NULL), + bg_cv_(&mutex_), + mem_(new MemTable(internal_comparator_)), + imm_(NULL), + logfile_(NULL), + logfile_number_(0), + log_(NULL), + tmp_batch_(new WriteBatch), + bg_compaction_scheduled_(false), + manual_compaction_(NULL) { + mem_->Ref(); + has_imm_.Release_Store(NULL); + + // Reserve ten files or so for other uses and give the rest to TableCache. + const int table_cache_size = options.max_open_files - 10; + table_cache_ = new TableCache(dbname_, &options_, table_cache_size); + + versions_ = new VersionSet(dbname_, &options_, table_cache_, + &internal_comparator_); +} + +DBImpl::~DBImpl() { + // Wait for background work to finish + mutex_.Lock(); + shutting_down_.Release_Store(this); // Any non-NULL value is ok + while (bg_compaction_scheduled_) { + bg_cv_.Wait(); + } + mutex_.Unlock(); + + if (db_lock_ != NULL) { + env_->UnlockFile(db_lock_); + } + + delete versions_; + if (mem_ != NULL) mem_->Unref(); + if (imm_ != NULL) imm_->Unref(); + delete tmp_batch_; + delete log_; + delete logfile_; + delete table_cache_; + + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } +} + +Status DBImpl::NewDB() { + VersionEdit new_db; + new_db.SetComparatorName(user_comparator()->Name()); + new_db.SetLogNumber(0); + new_db.SetNextFile(2); + new_db.SetLastSequence(0); + + const std::string manifest = DescriptorFileName(dbname_, 1); + WritableFile* file; + Status s = env_->NewWritableFile(manifest, &file); + if (!s.ok()) { + return s; + } + { + log::Writer log(file); + std::string record; + new_db.EncodeTo(&record); + s = log.AddRecord(record); + if (s.ok()) { + s = file->Close(); + } + } + delete file; + if (s.ok()) { + // Make "CURRENT" file that points to the new manifest file. + s = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(manifest); + } + return s; +} + +void DBImpl::MaybeIgnoreError(Status* s) const { + if (s->ok() || options_.paranoid_checks) { + // No change needed + } else { + Log(options_.info_log, "Ignoring error %s", s->ToString().c_str()); + *s = Status::OK(); + } +} + +void DBImpl::DeleteObsoleteFiles() { + // Make a set of all of the live files + std::set<uint64_t> live = pending_outputs_; + versions_->AddLiveFiles(&live); + + std::vector<std::string> filenames; + env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + bool keep = true; + switch (type) { + case kLogFile: + keep = ((number >= versions_->LogNumber()) || + (number == versions_->PrevLogNumber())); + break; + case kDescriptorFile: + // Keep my manifest file, and any newer incarnations' + // (in case there is a race that allows other incarnations) + keep = (number >= versions_->ManifestFileNumber()); + break; + case kTableFile: + keep = (live.find(number) != live.end()); + break; + case kTempFile: + // Any temp files that are currently being written to must + // be recorded in pending_outputs_, which is inserted into "live" + keep = (live.find(number) != live.end()); + break; + case kCurrentFile: + case kDBLockFile: + case kInfoLogFile: + keep = true; + break; + } + + if (!keep) { + if (type == kTableFile) { + table_cache_->Evict(number); + } + Log(options_.info_log, "Delete type=%d #%lld\n", + int(type), + static_cast<unsigned long long>(number)); + env_->DeleteFile(dbname_ + "/" + filenames[i]); + } + } + } +} + +Status DBImpl::Recover(VersionEdit* edit) { + mutex_.AssertHeld(); + + // Ignore error from CreateDir since the creation of the DB is + // committed only when the descriptor is created, and this directory + // may already exist from a previous failed creation attempt. + env_->CreateDir(dbname_); + assert(db_lock_ == NULL); + Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); + if (!s.ok()) { + return s; + } + + if (!env_->FileExists(CurrentFileName(dbname_))) { + if (options_.create_if_missing) { + s = NewDB(); + if (!s.ok()) { + return s; + } + } else { + return Status::InvalidArgument( + dbname_, "does not exist (create_if_missing is false)"); + } + } else { + if (options_.error_if_exists) { + return Status::InvalidArgument( + dbname_, "exists (error_if_exists is true)"); + } + } + + s = versions_->Recover(); + if (s.ok()) { + SequenceNumber max_sequence(0); + + // Recover from all newer log files than the ones named in the + // descriptor (new log files may have been added by the previous + // incarnation without registering them in the descriptor). + // + // Note that PrevLogNumber() is no longer used, but we pay + // attention to it in case we are recovering a database + // produced by an older version of leveldb. + const uint64_t min_log = versions_->LogNumber(); + const uint64_t prev_log = versions_->PrevLogNumber(); + std::vector<std::string> filenames; + s = env_->GetChildren(dbname_, &filenames); + if (!s.ok()) { + return s; + } + uint64_t number; + FileType type; + std::vector<uint64_t> logs; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) + && type == kLogFile + && ((number >= min_log) || (number == prev_log))) { + logs.push_back(number); + } + } + + // Recover in the order in which the logs were generated + std::sort(logs.begin(), logs.end()); + for (size_t i = 0; i < logs.size(); i++) { + s = RecoverLogFile(logs[i], edit, &max_sequence); + + // The previous incarnation may not have written any MANIFEST + // records after allocating this log number. So we manually + // update the file number allocation counter in VersionSet. + versions_->MarkFileNumberUsed(logs[i]); + } + + if (s.ok()) { + if (versions_->LastSequence() < max_sequence) { + versions_->SetLastSequence(max_sequence); + } + } + } + + return s; +} + +Status DBImpl::RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + const char* fname; + Status* status; // NULL if options_.paranoid_checks==false + virtual void Corruption(size_t bytes, const Status& s) { + Log(info_log, "%s%s: dropping %d bytes; %s", + (this->status == NULL ? "(ignoring error) " : ""), + fname, static_cast<int>(bytes), s.ToString().c_str()); + if (this->status != NULL && this->status->ok()) *this->status = s; + } + }; + + mutex_.AssertHeld(); + + // Open the log file + std::string fname = LogFileName(dbname_, log_number); + SequentialFile* file; + Status status = env_->NewSequentialFile(fname, &file); + if (!status.ok()) { + MaybeIgnoreError(&status); + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.fname = fname.c_str(); + reporter.status = (options_.paranoid_checks ? &status : NULL); + // We intentially make log::Reader do checksumming even if + // paranoid_checks==false so that corruptions cause entire commits + // to be skipped instead of propagating bad information (like overly + // large sequence numbers). + log::Reader reader(file, &reporter, true/*checksum*/, + 0/*initial_offset*/); + Log(options_.info_log, "Recovering log #%llu", + (unsigned long long) log_number); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = NULL; + while (reader.ReadRecord(&record, &scratch) && + status.ok()) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + + if (mem == NULL) { + mem = new MemTable(internal_comparator_); + mem->Ref(); + } + status = WriteBatchInternal::InsertInto(&batch, mem); + MaybeIgnoreError(&status); + if (!status.ok()) { + break; + } + const SequenceNumber last_seq = + WriteBatchInternal::Sequence(&batch) + + WriteBatchInternal::Count(&batch) - 1; + if (last_seq > *max_sequence) { + *max_sequence = last_seq; + } + + if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { + status = WriteLevel0Table(mem, edit, NULL); + if (!status.ok()) { + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + break; + } + mem->Unref(); + mem = NULL; + } + } + + if (status.ok() && mem != NULL) { + status = WriteLevel0Table(mem, edit, NULL); + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + } + + if (mem != NULL) mem->Unref(); + delete file; + return status; +} + +Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, + Version* base) { + mutex_.AssertHeld(); + const uint64_t start_micros = env_->NowMicros(); + FileMetaData meta; + meta.number = versions_->NewFileNumber(); + pending_outputs_.insert(meta.number); + Iterator* iter = mem->NewIterator(); + Log(options_.info_log, "Level-0 table #%llu: started", + (unsigned long long) meta.number); + + Status s; + { + mutex_.Unlock(); + s = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + mutex_.Lock(); + } + + Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s", + (unsigned long long) meta.number, + (unsigned long long) meta.file_size, + s.ToString().c_str()); + delete iter; + pending_outputs_.erase(meta.number); + + + // Note that if file_size is zero, the file has been deleted and + // should not be added to the manifest. + int level = 0; + if (s.ok() && meta.file_size > 0) { + const Slice min_user_key = meta.smallest.user_key(); + const Slice max_user_key = meta.largest.user_key(); + if (base != NULL) { + level = base->PickLevelForMemTableOutput(min_user_key, max_user_key); + } + edit->AddFile(level, meta.number, meta.file_size, + meta.smallest, meta.largest); + } + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros; + stats.bytes_written = meta.file_size; + stats_[level].Add(stats); + return s; +} + +Status DBImpl::CompactMemTable() { + mutex_.AssertHeld(); + assert(imm_ != NULL); + + // Save the contents of the memtable as a new Table + VersionEdit edit; + Version* base = versions_->current(); + base->Ref(); + Status s = WriteLevel0Table(imm_, &edit, base); + base->Unref(); + + if (s.ok() && shutting_down_.Acquire_Load()) { + s = Status::IOError("Deleting DB during memtable compaction"); + } + + // Replace immutable memtable with the generated Table + if (s.ok()) { + edit.SetPrevLogNumber(0); + edit.SetLogNumber(logfile_number_); // Earlier logs no longer needed + s = versions_->LogAndApply(&edit, &mutex_); + } + + if (s.ok()) { + // Commit to the new state + imm_->Unref(); + imm_ = NULL; + has_imm_.Release_Store(NULL); + DeleteObsoleteFiles(); + } + + return s; +} + +void DBImpl::CompactRange(const Slice* begin, const Slice* end) { + int max_level_with_files = 1; + { + MutexLock l(&mutex_); + Version* base = versions_->current(); + for (int level = 1; level < config::kNumLevels; level++) { + if (base->OverlapInLevel(level, begin, end)) { + max_level_with_files = level; + } + } + } + TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap + for (int level = 0; level < max_level_with_files; level++) { + TEST_CompactRange(level, begin, end); + } +} + +void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { + assert(level >= 0); + assert(level + 1 < config::kNumLevels); + + InternalKey begin_storage, end_storage; + + ManualCompaction manual; + manual.level = level; + manual.done = false; + if (begin == NULL) { + manual.begin = NULL; + } else { + begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek); + manual.begin = &begin_storage; + } + if (end == NULL) { + manual.end = NULL; + } else { + end_storage = InternalKey(*end, 0, static_cast<ValueType>(0)); + manual.end = &end_storage; + } + + MutexLock l(&mutex_); + while (!manual.done) { + while (manual_compaction_ != NULL) { + bg_cv_.Wait(); + } + manual_compaction_ = &manual; + MaybeScheduleCompaction(); + while (manual_compaction_ == &manual) { + bg_cv_.Wait(); + } + } +} + +Status DBImpl::TEST_CompactMemTable() { + // NULL batch means just wait for earlier writes to be done + Status s = Write(WriteOptions(), NULL); + if (s.ok()) { + // Wait until the compaction completes + MutexLock l(&mutex_); + while (imm_ != NULL && bg_error_.ok()) { + bg_cv_.Wait(); + } + if (imm_ != NULL) { + s = bg_error_; + } + } + return s; +} + +void DBImpl::MaybeScheduleCompaction() { + mutex_.AssertHeld(); + if (bg_compaction_scheduled_) { + // Already scheduled + } else if (shutting_down_.Acquire_Load()) { + // DB is being deleted; no more background compactions + } else if (imm_ == NULL && + manual_compaction_ == NULL && + !versions_->NeedsCompaction()) { + // No work to be done + } else { + bg_compaction_scheduled_ = true; + env_->Schedule(&DBImpl::BGWork, this); + } +} + +void DBImpl::BGWork(void* db) { + reinterpret_cast<DBImpl*>(db)->BackgroundCall(); +} + +void DBImpl::BackgroundCall() { + MutexLock l(&mutex_); + assert(bg_compaction_scheduled_); + if (!shutting_down_.Acquire_Load()) { + Status s = BackgroundCompaction(); + if (!s.ok()) { + // Wait a little bit before retrying background compaction in + // case this is an environmental problem and we do not want to + // chew up resources for failed compactions for the duration of + // the problem. + bg_cv_.SignalAll(); // In case a waiter can proceed despite the error + Log(options_.info_log, "Waiting after background compaction error: %s", + s.ToString().c_str()); + mutex_.Unlock(); + env_->SleepForMicroseconds(1000000); + mutex_.Lock(); + } + } + + bg_compaction_scheduled_ = false; + + // Previous compaction may have produced too many files in a level, + // so reschedule another compaction if needed. + MaybeScheduleCompaction(); + bg_cv_.SignalAll(); +} + +Status DBImpl::BackgroundCompaction() { + mutex_.AssertHeld(); + + if (imm_ != NULL) { + return CompactMemTable(); + } + + Compaction* c; + bool is_manual = (manual_compaction_ != NULL); + InternalKey manual_end; + if (is_manual) { + ManualCompaction* m = manual_compaction_; + c = versions_->CompactRange(m->level, m->begin, m->end); + m->done = (c == NULL); + if (c != NULL) { + manual_end = c->input(0, c->num_input_files(0) - 1)->largest; + } + Log(options_.info_log, + "Manual compaction at level-%d from %s .. %s; will stop at %s\n", + m->level, + (m->begin ? m->begin->DebugString().c_str() : "(begin)"), + (m->end ? m->end->DebugString().c_str() : "(end)"), + (m->done ? "(end)" : manual_end.DebugString().c_str())); + } else { + c = versions_->PickCompaction(); + } + + Status status; + if (c == NULL) { + // Nothing to do + } else if (!is_manual && c->IsTrivialMove()) { + // Move file to next level + assert(c->num_input_files(0) == 1); + FileMetaData* f = c->input(0, 0); + c->edit()->DeleteFile(c->level(), f->number); + c->edit()->AddFile(c->level() + 1, f->number, f->file_size, + f->smallest, f->largest); + status = versions_->LogAndApply(c->edit(), &mutex_); + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n", + static_cast<unsigned long long>(f->number), + c->level() + 1, + static_cast<unsigned long long>(f->file_size), + status.ToString().c_str(), + versions_->LevelSummary(&tmp)); + } else { + CompactionState* compact = new CompactionState(c); + status = DoCompactionWork(compact); + CleanupCompaction(compact); + c->ReleaseInputs(); + DeleteObsoleteFiles(); + } + delete c; + + if (status.ok()) { + // Done + } else if (shutting_down_.Acquire_Load()) { + // Ignore compaction errors found during shutting down + } else { + Log(options_.info_log, + "Compaction error: %s", status.ToString().c_str()); + if (options_.paranoid_checks && bg_error_.ok()) { + bg_error_ = status; + } + } + + if (is_manual) { + ManualCompaction* m = manual_compaction_; + if (!status.ok()) { + m->done = true; + } + if (!m->done) { + // We only compacted part of the requested range. Update *m + // to the range that is left to be compacted. + m->tmp_storage = manual_end; + m->begin = &m->tmp_storage; + } + manual_compaction_ = NULL; + } + return status; +} + +void DBImpl::CleanupCompaction(CompactionState* compact) { + mutex_.AssertHeld(); + if (compact->builder != NULL) { + // May happen if we get a shutdown call in the middle of compaction + compact->builder->Abandon(); + delete compact->builder; + } else { + assert(compact->outfile == NULL); + } + delete compact->outfile; + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + pending_outputs_.erase(out.number); + } + delete compact; +} + +Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { + assert(compact != NULL); + assert(compact->builder == NULL); + uint64_t file_number; + { + mutex_.Lock(); + file_number = versions_->NewFileNumber(); + pending_outputs_.insert(file_number); + CompactionState::Output out; + out.number = file_number; + out.smallest.Clear(); + out.largest.Clear(); + compact->outputs.push_back(out); + mutex_.Unlock(); + } + + // Make the output file + std::string fname = TableFileName(dbname_, file_number); + Status s = env_->NewWritableFile(fname, &compact->outfile); + if (s.ok()) { + compact->builder = new TableBuilder(options_, compact->outfile); + } + return s; +} + +Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, + Iterator* input) { + assert(compact != NULL); + assert(compact->outfile != NULL); + assert(compact->builder != NULL); + + const uint64_t output_number = compact->current_output()->number; + assert(output_number != 0); + + // Check for iterator errors + Status s = input->status(); + const uint64_t current_entries = compact->builder->NumEntries(); + if (s.ok()) { + s = compact->builder->Finish(); + } else { + compact->builder->Abandon(); + } + const uint64_t current_bytes = compact->builder->FileSize(); + compact->current_output()->file_size = current_bytes; + compact->total_bytes += current_bytes; + delete compact->builder; + compact->builder = NULL; + + // Finish and check for file errors + if (s.ok()) { + s = compact->outfile->Sync(); + } + if (s.ok()) { + s = compact->outfile->Close(); + } + delete compact->outfile; + compact->outfile = NULL; + + if (s.ok() && current_entries > 0) { + // Verify that the table is usable + Iterator* iter = table_cache_->NewIterator(ReadOptions(), + output_number, + current_bytes); + s = iter->status(); + delete iter; + if (s.ok()) { + Log(options_.info_log, + "Generated table #%llu: %lld keys, %lld bytes", + (unsigned long long) output_number, + (unsigned long long) current_entries, + (unsigned long long) current_bytes); + } + } + return s; +} + + +Status DBImpl::InstallCompactionResults(CompactionState* compact) { + mutex_.AssertHeld(); + Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1, + static_cast<long long>(compact->total_bytes)); + + // Add compaction outputs + compact->compaction->AddInputDeletions(compact->compaction->edit()); + const int level = compact->compaction->level(); + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + compact->compaction->edit()->AddFile( + level + 1, + out.number, out.file_size, out.smallest, out.largest); + } + return versions_->LogAndApply(compact->compaction->edit(), &mutex_); +} + +Status DBImpl::DoCompactionWork(CompactionState* compact) { + const uint64_t start_micros = env_->NowMicros(); + int64_t imm_micros = 0; // Micros spent doing imm_ compactions + + Log(options_.info_log, "Compacting %d@%d + %d@%d files", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1); + + assert(versions_->NumLevelFiles(compact->compaction->level()) > 0); + assert(compact->builder == NULL); + assert(compact->outfile == NULL); + if (snapshots_.empty()) { + compact->smallest_snapshot = versions_->LastSequence(); + } else { + compact->smallest_snapshot = snapshots_.oldest()->number_; + } + + // Release mutex while we're actually doing the compaction work + mutex_.Unlock(); + + Iterator* input = versions_->MakeInputIterator(compact->compaction); + input->SeekToFirst(); + Status status; + ParsedInternalKey ikey; + std::string current_user_key; + bool has_current_user_key = false; + SequenceNumber last_sequence_for_key = kMaxSequenceNumber; + for (; input->Valid() && !shutting_down_.Acquire_Load(); ) { + // Prioritize immutable compaction work + if (has_imm_.NoBarrier_Load() != NULL) { + const uint64_t imm_start = env_->NowMicros(); + mutex_.Lock(); + if (imm_ != NULL) { + CompactMemTable(); + bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary + } + mutex_.Unlock(); + imm_micros += (env_->NowMicros() - imm_start); + } + + Slice key = input->key(); + if (compact->compaction->ShouldStopBefore(key) && + compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + + // Handle key/value, add to state, etc. + bool drop = false; + if (!ParseInternalKey(key, &ikey)) { + // Do not hide error keys + current_user_key.clear(); + has_current_user_key = false; + last_sequence_for_key = kMaxSequenceNumber; + } else { + if (!has_current_user_key || + user_comparator()->Compare(ikey.user_key, + Slice(current_user_key)) != 0) { + // First occurrence of this user key + current_user_key.assign(ikey.user_key.data(), ikey.user_key.size()); + has_current_user_key = true; + last_sequence_for_key = kMaxSequenceNumber; + } + + if (last_sequence_for_key <= compact->smallest_snapshot) { + // Hidden by an newer entry for same user key + drop = true; // (A) + } else if (ikey.type == kTypeDeletion && + ikey.sequence <= compact->smallest_snapshot && + compact->compaction->IsBaseLevelForKey(ikey.user_key)) { + // For this user key: + // (1) there is no data in higher levels + // (2) data in lower levels will have larger sequence numbers + // (3) data in layers that are being compacted here and have + // smaller sequence numbers will be dropped in the next + // few iterations of this loop (by rule (A) above). + // Therefore this deletion marker is obsolete and can be dropped. + drop = true; + } + + last_sequence_for_key = ikey.sequence; + } +#if 0 + Log(options_.info_log, + " Compact: %s, seq %d, type: %d %d, drop: %d, is_base: %d, " + "%d smallest_snapshot: %d", + ikey.user_key.ToString().c_str(), + (int)ikey.sequence, ikey.type, kTypeValue, drop, + compact->compaction->IsBaseLevelForKey(ikey.user_key), + (int)last_sequence_for_key, (int)compact->smallest_snapshot); +#endif + + if (!drop) { + // Open output file if necessary + if (compact->builder == NULL) { + status = OpenCompactionOutputFile(compact); + if (!status.ok()) { + break; + } + } + if (compact->builder->NumEntries() == 0) { + compact->current_output()->smallest.DecodeFrom(key); + } + compact->current_output()->largest.DecodeFrom(key); + compact->builder->Add(key, input->value()); + + // Close output file if it is big enough + if (compact->builder->FileSize() >= + compact->compaction->MaxOutputFileSize()) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + } + + input->Next(); + } + + if (status.ok() && shutting_down_.Acquire_Load()) { + status = Status::IOError("Deleting DB during compaction"); + } + if (status.ok() && compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + } + if (status.ok()) { + status = input->status(); + } + delete input; + input = NULL; + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros - imm_micros; + for (int which = 0; which < 2; which++) { + for (int i = 0; i < compact->compaction->num_input_files(which); i++) { + stats.bytes_read += compact->compaction->input(which, i)->file_size; + } + } + for (size_t i = 0; i < compact->outputs.size(); i++) { + stats.bytes_written += compact->outputs[i].file_size; + } + + mutex_.Lock(); + stats_[compact->compaction->level() + 1].Add(stats); + + if (status.ok()) { + status = InstallCompactionResults(compact); + } + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, + "compacted to: %s", versions_->LevelSummary(&tmp)); + return status; +} + +namespace { +struct IterState { + port::Mutex* mu; + Version* version; + MemTable* mem; + MemTable* imm; +}; + +static void CleanupIteratorState(void* arg1, void* arg2) { + IterState* state = reinterpret_cast<IterState*>(arg1); + state->mu->Lock(); + state->mem->Unref(); + if (state->imm != NULL) state->imm->Unref(); + state->version->Unref(); + state->mu->Unlock(); + delete state; +} +} // namespace + +Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, + SequenceNumber* latest_snapshot) { + IterState* cleanup = new IterState; + mutex_.Lock(); + *latest_snapshot = versions_->LastSequence(); + + // Collect together all needed child iterators + std::vector<Iterator*> list; + list.push_back(mem_->NewIterator()); + mem_->Ref(); + if (imm_ != NULL) { + list.push_back(imm_->NewIterator()); + imm_->Ref(); + } + versions_->current()->AddIterators(options, &list); + Iterator* internal_iter = + NewMergingIterator(&internal_comparator_, &list[0], list.size()); + versions_->current()->Ref(); + + cleanup->mu = &mutex_; + cleanup->mem = mem_; + cleanup->imm = imm_; + cleanup->version = versions_->current(); + internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + + mutex_.Unlock(); + return internal_iter; +} + +Iterator* DBImpl::TEST_NewInternalIterator() { + SequenceNumber ignored; + return NewInternalIterator(ReadOptions(), &ignored); +} + +int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { + MutexLock l(&mutex_); + return versions_->MaxNextLevelOverlappingBytes(); +} + +Status DBImpl::Get(const ReadOptions& options, + const Slice& key, + std::string* value) { + Status s; + MutexLock l(&mutex_); + SequenceNumber snapshot; + if (options.snapshot != NULL) { + snapshot = reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_; + } else { + snapshot = versions_->LastSequence(); + } + + MemTable* mem = mem_; + MemTable* imm = imm_; + Version* current = versions_->current(); + mem->Ref(); + if (imm != NULL) imm->Ref(); + current->Ref(); + + bool have_stat_update = false; + Version::GetStats stats; + + // Unlock while reading from files and memtables + { + mutex_.Unlock(); + // First look in the memtable, then in the immutable memtable (if any). + LookupKey lkey(key, snapshot); + if (mem->Get(lkey, value, &s)) { + // Done + } else if (imm != NULL && imm->Get(lkey, value, &s)) { + // Done + } else { + s = current->Get(options, lkey, value, &stats); + have_stat_update = true; + } + mutex_.Lock(); + } + + if (have_stat_update && current->UpdateStats(stats)) { + MaybeScheduleCompaction(); + } + mem->Unref(); + if (imm != NULL) imm->Unref(); + current->Unref(); + return s; +} + +Iterator* DBImpl::NewIterator(const ReadOptions& options) { + SequenceNumber latest_snapshot; + Iterator* internal_iter = NewInternalIterator(options, &latest_snapshot); + return NewDBIterator( + &dbname_, env_, user_comparator(), internal_iter, + (options.snapshot != NULL + ? reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_ + : latest_snapshot)); +} + +const Snapshot* DBImpl::GetSnapshot() { + MutexLock l(&mutex_); + return snapshots_.New(versions_->LastSequence()); +} + +void DBImpl::ReleaseSnapshot(const Snapshot* s) { + MutexLock l(&mutex_); + snapshots_.Delete(reinterpret_cast<const SnapshotImpl*>(s)); +} + +// Convenience methods +Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) { + return DB::Put(o, key, val); +} + +Status DBImpl::Delete(const WriteOptions& options, const Slice& key) { + return DB::Delete(options, key); +} + +Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { + Writer w(&mutex_); + w.batch = my_batch; + w.sync = options.sync; + w.done = false; + + MutexLock l(&mutex_); + writers_.push_back(&w); + while (!w.done && &w != writers_.front()) { + w.cv.Wait(); + } + if (w.done) { + return w.status; + } + + // May temporarily unlock and wait. + Status status = MakeRoomForWrite(my_batch == NULL); + uint64_t last_sequence = versions_->LastSequence(); + Writer* last_writer = &w; + if (status.ok() && my_batch != NULL) { // NULL batch is for compactions + WriteBatch* updates = BuildBatchGroup(&last_writer); + WriteBatchInternal::SetSequence(updates, last_sequence + 1); + last_sequence += WriteBatchInternal::Count(updates); + + // Add to log and apply to memtable. We can release the lock + // during this phase since &w is currently responsible for logging + // and protects against concurrent loggers and concurrent writes + // into mem_. + { + mutex_.Unlock(); + status = log_->AddRecord(WriteBatchInternal::Contents(updates)); + if (status.ok() && options.sync) { + status = logfile_->Sync(); + } + if (status.ok()) { + status = WriteBatchInternal::InsertInto(updates, mem_); + } + mutex_.Lock(); + } + if (updates == tmp_batch_) tmp_batch_->Clear(); + + versions_->SetLastSequence(last_sequence); + } + + while (true) { + Writer* ready = writers_.front(); + writers_.pop_front(); + if (ready != &w) { + ready->status = status; + ready->done = true; + ready->cv.Signal(); + } + if (ready == last_writer) break; + } + + // Notify new head of write queue + if (!writers_.empty()) { + writers_.front()->cv.Signal(); + } + + return status; +} + +// REQUIRES: Writer list must be non-empty +// REQUIRES: First writer must have a non-NULL batch +WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { + assert(!writers_.empty()); + Writer* first = writers_.front(); + WriteBatch* result = first->batch; + assert(result != NULL); + + size_t size = WriteBatchInternal::ByteSize(first->batch); + + // Allow the group to grow up to a maximum size, but if the + // original write is small, limit the growth so we do not slow + // down the small write too much. + size_t max_size = 1 << 20; + if (size <= (128<<10)) { + max_size = size + (128<<10); + } + + *last_writer = first; + std::deque<Writer*>::iterator iter = writers_.begin(); + ++iter; // Advance past "first" + for (; iter != writers_.end(); ++iter) { + Writer* w = *iter; + if (w->sync && !first->sync) { + // Do not include a sync write into a batch handled by a non-sync write. + break; + } + + if (w->batch != NULL) { + size += WriteBatchInternal::ByteSize(w->batch); + if (size > max_size) { + // Do not make batch too big + break; + } + + // Append to *reuslt + if (result == first->batch) { + // Switch to temporary batch instead of disturbing caller's batch + result = tmp_batch_; + assert(WriteBatchInternal::Count(result) == 0); + WriteBatchInternal::Append(result, first->batch); + } + WriteBatchInternal::Append(result, w->batch); + } + *last_writer = w; + } + return result; +} + +// REQUIRES: mutex_ is held +// REQUIRES: this thread is currently at the front of the writer queue +Status DBImpl::MakeRoomForWrite(bool force) { + mutex_.AssertHeld(); + assert(!writers_.empty()); + bool allow_delay = !force; + Status s; + while (true) { + if (!bg_error_.ok()) { + // Yield previous error + s = bg_error_; + break; + } else if ( + allow_delay && + versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) { + // We are getting close to hitting a hard limit on the number of + // L0 files. Rather than delaying a single write by several + // seconds when we hit the hard limit, start delaying each + // individual write by 1ms to reduce latency variance. Also, + // this delay hands over some CPU to the compaction thread in + // case it is sharing the same core as the writer. + mutex_.Unlock(); + env_->SleepForMicroseconds(1000); + allow_delay = false; // Do not delay a single write more than once + mutex_.Lock(); + } else if (!force && + (mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) { + // There is room in current memtable + break; + } else if (imm_ != NULL) { + // We have filled up the current memtable, but the previous + // one is still being compacted, so we wait. + bg_cv_.Wait(); + } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { + // There are too many level-0 files. + Log(options_.info_log, "waiting...\n"); + bg_cv_.Wait(); + } else { + // Attempt to switch to a new memtable and trigger compaction of old + assert(versions_->PrevLogNumber() == 0); + uint64_t new_log_number = versions_->NewFileNumber(); + WritableFile* lfile = NULL; + s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile); + if (!s.ok()) { + // Avoid chewing through file number space in a tight loop. + versions_->ReuseFileNumber(new_log_number); + break; + } + delete log_; + delete logfile_; + logfile_ = lfile; + logfile_number_ = new_log_number; + log_ = new log::Writer(lfile); + imm_ = mem_; + has_imm_.Release_Store(imm_); + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + force = false; // Do not force another compaction if have room + MaybeScheduleCompaction(); + } + } + return s; +} + +bool DBImpl::GetProperty(const Slice& property, std::string* value) { + value->clear(); + + MutexLock l(&mutex_); + Slice in = property; + Slice prefix("leveldb."); + if (!in.starts_with(prefix)) return false; + in.remove_prefix(prefix.size()); + + if (in.starts_with("num-files-at-level")) { + in.remove_prefix(strlen("num-files-at-level")); + uint64_t level; + bool ok = ConsumeDecimalNumber(&in, &level) && in.empty(); + if (!ok || level >= config::kNumLevels) { + return false; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "%d", + versions_->NumLevelFiles(static_cast<int>(level))); + *value = buf; + return true; + } + } else if (in == "stats") { + char buf[200]; + snprintf(buf, sizeof(buf), + " Compactions\n" + "Level Files Size(MB) Time(sec) Read(MB) Write(MB)\n" + "--------------------------------------------------\n" + ); + value->append(buf); + for (int level = 0; level < config::kNumLevels; level++) { + int files = versions_->NumLevelFiles(level); + if (stats_[level].micros > 0 || files > 0) { + snprintf( + buf, sizeof(buf), + "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", + level, + files, + versions_->NumLevelBytes(level) / 1048576.0, + stats_[level].micros / 1e6, + stats_[level].bytes_read / 1048576.0, + stats_[level].bytes_written / 1048576.0); + value->append(buf); + } + } + return true; + } else if (in == "sstables") { + *value = versions_->current()->DebugString(); + return true; + } + + return false; +} + +void DBImpl::GetApproximateSizes( + const Range* range, int n, + uint64_t* sizes) { + // TODO(opt): better implementation + Version* v; + { + MutexLock l(&mutex_); + versions_->current()->Ref(); + v = versions_->current(); + } + + for (int i = 0; i < n; i++) { + // Convert user_key into a corresponding internal key. + InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek); + uint64_t start = versions_->ApproximateOffsetOf(v, k1); + uint64_t limit = versions_->ApproximateOffsetOf(v, k2); + sizes[i] = (limit >= start ? limit - start : 0); + } + + { + MutexLock l(&mutex_); + v->Unref(); + } +} + +// Default implementations of convenience methods that subclasses of DB +// can call if they wish +Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) { + WriteBatch batch; + batch.Put(key, value); + return Write(opt, &batch); +} + +Status DB::Delete(const WriteOptions& opt, const Slice& key) { + WriteBatch batch; + batch.Delete(key); + return Write(opt, &batch); +} + +DB::~DB() { } + +Status DB::Open(const Options& options, const std::string& dbname, + DB** dbptr) { + *dbptr = NULL; + + DBImpl* impl = new DBImpl(options, dbname); + impl->mutex_.Lock(); + VersionEdit edit; + Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists + if (s.ok()) { + uint64_t new_log_number = impl->versions_->NewFileNumber(); + WritableFile* lfile; + s = options.env->NewWritableFile(LogFileName(dbname, new_log_number), + &lfile); + if (s.ok()) { + edit.SetLogNumber(new_log_number); + impl->logfile_ = lfile; + impl->logfile_number_ = new_log_number; + impl->log_ = new log::Writer(lfile); + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + if (s.ok()) { + impl->DeleteObsoleteFiles(); + impl->MaybeScheduleCompaction(); + } + } + impl->mutex_.Unlock(); + if (s.ok()) { + *dbptr = impl; + } else { + delete impl; + } + return s; +} + +Snapshot::~Snapshot() { +} + +Status DestroyDB(const std::string& dbname, const Options& options) { + Env* env = options.env; + std::vector<std::string> filenames; + // Ignore error in case directory does not exist + env->GetChildren(dbname, &filenames); + if (filenames.empty()) { + return Status::OK(); + } + + FileLock* lock; + const std::string lockname = LockFileName(dbname); + Status result = env->LockFile(lockname, &lock); + if (result.ok()) { + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type != kDBLockFile) { // Lock file will be deleted at end + Status del = env->DeleteFile(dbname + "/" + filenames[i]); + if (result.ok() && !del.ok()) { + result = del; + } + } + } + env->UnlockFile(lock); // Ignore error since state is already gone + env->DeleteFile(lockname); + env->DeleteDir(dbname); // Ignore error in case dir contains other files + } + return result; +} + +} // namespace leveldb diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h new file mode 100644 index 0000000000..8d2bb3405f --- /dev/null +++ b/src/leveldb/db/db_impl.h @@ -0,0 +1,194 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_ +#define STORAGE_LEVELDB_DB_DB_IMPL_H_ + +#include <deque> +#include <set> +#include "db/dbformat.h" +#include "db/log_writer.h" +#include "db/snapshot.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "port/port.h" + +namespace leveldb { + +class MemTable; +class TableCache; +class Version; +class VersionEdit; +class VersionSet; + +class DBImpl : public DB { + public: + DBImpl(const Options& options, const std::string& dbname); + virtual ~DBImpl(); + + // Implementations of the DB interface + virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value); + virtual Status Delete(const WriteOptions&, const Slice& key); + virtual Status Write(const WriteOptions& options, WriteBatch* updates); + virtual Status Get(const ReadOptions& options, + const Slice& key, + std::string* value); + virtual Iterator* NewIterator(const ReadOptions&); + virtual const Snapshot* GetSnapshot(); + virtual void ReleaseSnapshot(const Snapshot* snapshot); + virtual bool GetProperty(const Slice& property, std::string* value); + virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes); + virtual void CompactRange(const Slice* begin, const Slice* end); + + // Extra methods (for testing) that are not in the public DB interface + + // Compact any files in the named level that overlap [*begin,*end] + void TEST_CompactRange(int level, const Slice* begin, const Slice* end); + + // Force current memtable contents to be compacted. + Status TEST_CompactMemTable(); + + // Return an internal iterator over the current state of the database. + // The keys of this iterator are internal keys (see format.h). + // The returned iterator should be deleted when no longer needed. + Iterator* TEST_NewInternalIterator(); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t TEST_MaxNextLevelOverlappingBytes(); + + private: + friend class DB; + struct CompactionState; + struct Writer; + + Iterator* NewInternalIterator(const ReadOptions&, + SequenceNumber* latest_snapshot); + + Status NewDB(); + + // Recover the descriptor from persistent storage. May do a significant + // amount of work to recover recently logged updates. Any changes to + // be made to the descriptor are added to *edit. + Status Recover(VersionEdit* edit); + + void MaybeIgnoreError(Status* s) const; + + // Delete any unneeded files and stale in-memory entries. + void DeleteObsoleteFiles(); + + // Compact the in-memory write buffer to disk. Switches to a new + // log-file/memtable and writes a new descriptor iff successful. + Status CompactMemTable(); + + Status RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence); + + Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base); + + Status MakeRoomForWrite(bool force /* compact even if there is room? */); + WriteBatch* BuildBatchGroup(Writer** last_writer); + + void MaybeScheduleCompaction(); + static void BGWork(void* db); + void BackgroundCall(); + Status BackgroundCompaction(); + void CleanupCompaction(CompactionState* compact); + Status DoCompactionWork(CompactionState* compact); + + Status OpenCompactionOutputFile(CompactionState* compact); + Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input); + Status InstallCompactionResults(CompactionState* compact); + + // Constant after construction + Env* const env_; + const InternalKeyComparator internal_comparator_; + const InternalFilterPolicy internal_filter_policy_; + const Options options_; // options_.comparator == &internal_comparator_ + bool owns_info_log_; + bool owns_cache_; + const std::string dbname_; + + // table_cache_ provides its own synchronization + TableCache* table_cache_; + + // Lock over the persistent DB state. Non-NULL iff successfully acquired. + FileLock* db_lock_; + + // State below is protected by mutex_ + port::Mutex mutex_; + port::AtomicPointer shutting_down_; + port::CondVar bg_cv_; // Signalled when background work finishes + MemTable* mem_; + MemTable* imm_; // Memtable being compacted + port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ + WritableFile* logfile_; + uint64_t logfile_number_; + log::Writer* log_; + + // Queue of writers. + std::deque<Writer*> writers_; + WriteBatch* tmp_batch_; + + SnapshotList snapshots_; + + // Set of table files to protect from deletion because they are + // part of ongoing compactions. + std::set<uint64_t> pending_outputs_; + + // Has a background compaction been scheduled or is running? + bool bg_compaction_scheduled_; + + // Information for a manual compaction + struct ManualCompaction { + int level; + bool done; + const InternalKey* begin; // NULL means beginning of key range + const InternalKey* end; // NULL means end of key range + InternalKey tmp_storage; // Used to keep track of compaction progress + }; + ManualCompaction* manual_compaction_; + + VersionSet* versions_; + + // Have we encountered a background error in paranoid mode? + Status bg_error_; + + // Per level compaction stats. stats_[level] stores the stats for + // compactions that produced data for the specified "level". + struct CompactionStats { + int64_t micros; + int64_t bytes_read; + int64_t bytes_written; + + CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { } + + void Add(const CompactionStats& c) { + this->micros += c.micros; + this->bytes_read += c.bytes_read; + this->bytes_written += c.bytes_written; + } + }; + CompactionStats stats_[config::kNumLevels]; + + // No copying allowed + DBImpl(const DBImpl&); + void operator=(const DBImpl&); + + const Comparator* user_comparator() const { + return internal_comparator_.user_comparator(); + } +}; + +// Sanitize db options. The caller should delete result.info_log if +// it is not equal to src.info_log. +extern Options SanitizeOptions(const std::string& db, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_IMPL_H_ diff --git a/src/leveldb/db/db_iter.cc b/src/leveldb/db/db_iter.cc new file mode 100644 index 0000000000..87dca2ded4 --- /dev/null +++ b/src/leveldb/db/db_iter.cc @@ -0,0 +1,299 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_iter.h" + +#include "db/filename.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" + +namespace leveldb { + +#if 0 +static void DumpInternalIter(Iterator* iter) { + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey k; + if (!ParseInternalKey(iter->key(), &k)) { + fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str()); + } else { + fprintf(stderr, "@ '%s'\n", k.DebugString().c_str()); + } + } +} +#endif + +namespace { + +// Memtables and sstables that make the DB representation contain +// (userkey,seq,type) => uservalue entries. DBIter +// combines multiple entries for the same userkey found in the DB +// representation into a single entry while accounting for sequence +// numbers, deletion markers, overwrites, etc. +class DBIter: public Iterator { + public: + // Which direction is the iterator currently moving? + // (1) When moving forward, the internal iterator is positioned at + // the exact entry that yields this->key(), this->value() + // (2) When moving backwards, the internal iterator is positioned + // just before all entries whose user key == this->key(). + enum Direction { + kForward, + kReverse + }; + + DBIter(const std::string* dbname, Env* env, + const Comparator* cmp, Iterator* iter, SequenceNumber s) + : dbname_(dbname), + env_(env), + user_comparator_(cmp), + iter_(iter), + sequence_(s), + direction_(kForward), + valid_(false) { + } + virtual ~DBIter() { + delete iter_; + } + virtual bool Valid() const { return valid_; } + virtual Slice key() const { + assert(valid_); + return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_; + } + virtual Slice value() const { + assert(valid_); + return (direction_ == kForward) ? iter_->value() : saved_value_; + } + virtual Status status() const { + if (status_.ok()) { + return iter_->status(); + } else { + return status_; + } + } + + virtual void Next(); + virtual void Prev(); + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + + private: + void FindNextUserEntry(bool skipping, std::string* skip); + void FindPrevUserEntry(); + bool ParseKey(ParsedInternalKey* key); + + inline void SaveKey(const Slice& k, std::string* dst) { + dst->assign(k.data(), k.size()); + } + + inline void ClearSavedValue() { + if (saved_value_.capacity() > 1048576) { + std::string empty; + swap(empty, saved_value_); + } else { + saved_value_.clear(); + } + } + + const std::string* const dbname_; + Env* const env_; + const Comparator* const user_comparator_; + Iterator* const iter_; + SequenceNumber const sequence_; + + Status status_; + std::string saved_key_; // == current key when direction_==kReverse + std::string saved_value_; // == current raw value when direction_==kReverse + Direction direction_; + bool valid_; + + // No copying allowed + DBIter(const DBIter&); + void operator=(const DBIter&); +}; + +inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { + if (!ParseInternalKey(iter_->key(), ikey)) { + status_ = Status::Corruption("corrupted internal key in DBIter"); + return false; + } else { + return true; + } +} + +void DBIter::Next() { + assert(valid_); + + if (direction_ == kReverse) { // Switch directions? + direction_ = kForward; + // iter_ is pointing just before the entries for this->key(), + // so advance into the range of entries for this->key() and then + // use the normal skipping code below. + if (!iter_->Valid()) { + iter_->SeekToFirst(); + } else { + iter_->Next(); + } + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + return; + } + } + + // Temporarily use saved_key_ as storage for key to skip. + std::string* skip = &saved_key_; + SaveKey(ExtractUserKey(iter_->key()), skip); + FindNextUserEntry(true, skip); +} + +void DBIter::FindNextUserEntry(bool skipping, std::string* skip) { + // Loop until we hit an acceptable entry to yield + assert(iter_->Valid()); + assert(direction_ == kForward); + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + switch (ikey.type) { + case kTypeDeletion: + // Arrange to skip all upcoming entries for this key since + // they are hidden by this deletion. + SaveKey(ikey.user_key, skip); + skipping = true; + break; + case kTypeValue: + if (skipping && + user_comparator_->Compare(ikey.user_key, *skip) <= 0) { + // Entry hidden + } else { + valid_ = true; + saved_key_.clear(); + return; + } + break; + } + } + iter_->Next(); + } while (iter_->Valid()); + saved_key_.clear(); + valid_ = false; +} + +void DBIter::Prev() { + assert(valid_); + + if (direction_ == kForward) { // Switch directions? + // iter_ is pointing at the current entry. Scan backwards until + // the key changes so we can use the normal reverse scanning code. + assert(iter_->Valid()); // Otherwise valid_ would have been false + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + while (true) { + iter_->Prev(); + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + return; + } + if (user_comparator_->Compare(ExtractUserKey(iter_->key()), + saved_key_) < 0) { + break; + } + } + direction_ = kReverse; + } + + FindPrevUserEntry(); +} + +void DBIter::FindPrevUserEntry() { + assert(direction_ == kReverse); + + ValueType value_type = kTypeDeletion; + if (iter_->Valid()) { + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + if ((value_type != kTypeDeletion) && + user_comparator_->Compare(ikey.user_key, saved_key_) < 0) { + // We encountered a non-deleted value in entries for previous keys, + break; + } + value_type = ikey.type; + if (value_type == kTypeDeletion) { + saved_key_.clear(); + ClearSavedValue(); + } else { + Slice raw_value = iter_->value(); + if (saved_value_.capacity() > raw_value.size() + 1048576) { + std::string empty; + swap(empty, saved_value_); + } + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + saved_value_.assign(raw_value.data(), raw_value.size()); + } + } + iter_->Prev(); + } while (iter_->Valid()); + } + + if (value_type == kTypeDeletion) { + // End + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + direction_ = kForward; + } else { + valid_ = true; + } +} + +void DBIter::Seek(const Slice& target) { + direction_ = kForward; + ClearSavedValue(); + saved_key_.clear(); + AppendInternalKey( + &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek)); + iter_->Seek(saved_key_); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToFirst() { + direction_ = kForward; + ClearSavedValue(); + iter_->SeekToFirst(); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToLast() { + direction_ = kReverse; + ClearSavedValue(); + iter_->SeekToLast(); + FindPrevUserEntry(); +} + +} // anonymous namespace + +Iterator* NewDBIterator( + const std::string* dbname, + Env* env, + const Comparator* user_key_comparator, + Iterator* internal_iter, + const SequenceNumber& sequence) { + return new DBIter(dbname, env, user_key_comparator, internal_iter, sequence); +} + +} // namespace leveldb diff --git a/src/leveldb/db/db_iter.h b/src/leveldb/db/db_iter.h new file mode 100644 index 0000000000..d9e1b174ab --- /dev/null +++ b/src/leveldb/db/db_iter.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_ITER_H_ +#define STORAGE_LEVELDB_DB_DB_ITER_H_ + +#include <stdint.h> +#include "leveldb/db.h" +#include "db/dbformat.h" + +namespace leveldb { + +// Return a new iterator that converts internal keys (yielded by +// "*internal_iter") that were live at the specified "sequence" number +// into appropriate user keys. +extern Iterator* NewDBIterator( + const std::string* dbname, + Env* env, + const Comparator* user_key_comparator, + Iterator* internal_iter, + const SequenceNumber& sequence); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_ITER_H_ diff --git a/src/leveldb/db/db_test.cc b/src/leveldb/db/db_test.cc new file mode 100644 index 0000000000..3744d0e117 --- /dev/null +++ b/src/leveldb/db/db_test.cc @@ -0,0 +1,1946 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static std::string RandomString(Random* rnd, int len) { + std::string r; + test::RandomString(rnd, len, &r); + return r; +} + +namespace { +class AtomicCounter { + private: + port::Mutex mu_; + int count_; + public: + AtomicCounter() : count_(0) { } + void Increment() { + MutexLock l(&mu_); + count_++; + } + int Read() { + MutexLock l(&mu_); + return count_; + } + void Reset() { + MutexLock l(&mu_); + count_ = 0; + } +}; +} + +// Special Env used to delay background operations +class SpecialEnv : public EnvWrapper { + public: + // sstable Sync() calls are blocked while this pointer is non-NULL. + port::AtomicPointer delay_sstable_sync_; + + // Simulate no-space errors while this pointer is non-NULL. + port::AtomicPointer no_space_; + + // Simulate non-writable file system while this pointer is non-NULL + port::AtomicPointer non_writable_; + + bool count_random_reads_; + AtomicCounter random_read_counter_; + + AtomicCounter sleep_counter_; + + explicit SpecialEnv(Env* base) : EnvWrapper(base) { + delay_sstable_sync_.Release_Store(NULL); + no_space_.Release_Store(NULL); + non_writable_.Release_Store(NULL); + count_random_reads_ = false; + } + + Status NewWritableFile(const std::string& f, WritableFile** r) { + class SSTableFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + + public: + SSTableFile(SpecialEnv* env, WritableFile* base) + : env_(env), + base_(base) { + } + ~SSTableFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->no_space_.Acquire_Load() != NULL) { + // Drop writes on the floor + return Status::OK(); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + while (env_->delay_sstable_sync_.Acquire_Load() != NULL) { + env_->SleepForMicroseconds(100000); + } + return base_->Sync(); + } + }; + + if (non_writable_.Acquire_Load() != NULL) { + return Status::IOError("simulated write error"); + } + + Status s = target()->NewWritableFile(f, r); + if (s.ok()) { + if (strstr(f.c_str(), ".sst") != NULL) { + *r = new SSTableFile(this, *r); + } + } + return s; + } + + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + class CountingFile : public RandomAccessFile { + private: + RandomAccessFile* target_; + AtomicCounter* counter_; + public: + CountingFile(RandomAccessFile* target, AtomicCounter* counter) + : target_(target), counter_(counter) { + } + virtual ~CountingFile() { delete target_; } + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + counter_->Increment(); + return target_->Read(offset, n, result, scratch); + } + }; + + Status s = target()->NewRandomAccessFile(f, r); + if (s.ok() && count_random_reads_) { + *r = new CountingFile(*r, &random_read_counter_); + } + return s; + } + + virtual void SleepForMicroseconds(int micros) { + sleep_counter_.Increment(); + target()->SleepForMicroseconds(micros); + } +}; + +class DBTest { + private: + const FilterPolicy* filter_policy_; + + // Sequence of option configurations to try + enum OptionConfig { + kDefault, + kFilter, + kUncompressed, + kEnd + }; + int option_config_; + + public: + std::string dbname_; + SpecialEnv* env_; + DB* db_; + + Options last_options_; + + DBTest() : option_config_(kDefault), + env_(new SpecialEnv(Env::Default())) { + filter_policy_ = NewBloomFilterPolicy(10); + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, Options()); + db_ = NULL; + Reopen(); + } + + ~DBTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete env_; + delete filter_policy_; + } + + // Switch to a fresh database with the next option configuration to + // test. Return false if there are no more configurations to test. + bool ChangeOptions() { + option_config_++; + if (option_config_ >= kEnd) { + return false; + } else { + DestroyAndReopen(); + return true; + } + } + + // Return the current option configuration. + Options CurrentOptions() { + Options options; + switch (option_config_) { + case kFilter: + options.filter_policy = filter_policy_; + break; + case kUncompressed: + options.compression = kNoCompression; + break; + default: + break; + } + return options; + } + + DBImpl* dbfull() { + return reinterpret_cast<DBImpl*>(db_); + } + + void Reopen(Options* options = NULL) { + ASSERT_OK(TryReopen(options)); + } + + void Close() { + delete db_; + db_ = NULL; + } + + void DestroyAndReopen(Options* options = NULL) { + delete db_; + db_ = NULL; + DestroyDB(dbname_, Options()); + ASSERT_OK(TryReopen(options)); + } + + Status TryReopen(Options* options) { + delete db_; + db_ = NULL; + Options opts; + if (options != NULL) { + opts = *options; + } else { + opts = CurrentOptions(); + opts.create_if_missing = true; + } + last_options_ = opts; + + return DB::Open(opts, dbname_, &db_); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + Status Delete(const std::string& k) { + return db_->Delete(WriteOptions(), k); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + ReadOptions options; + options.snapshot = snapshot; + std::string result; + Status s = db_->Get(options, k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + // Return a string that contains all key,value pairs in order, + // formatted like "(k1->v1)(k2->v2)". + std::string Contents() { + std::vector<std::string> forward; + std::string result; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + std::string s = IterStatus(iter); + result.push_back('('); + result.append(s); + result.push_back(')'); + forward.push_back(s); + } + + // Check reverse iteration results are the reverse of forward results + int matched = 0; + for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { + ASSERT_LT(matched, forward.size()); + ASSERT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]); + matched++; + } + ASSERT_EQ(matched, forward.size()); + + delete iter; + return result; + } + + std::string AllEntriesFor(const Slice& user_key) { + Iterator* iter = dbfull()->TEST_NewInternalIterator(); + InternalKey target(user_key, kMaxSequenceNumber, kTypeValue); + iter->Seek(target.Encode()); + std::string result; + if (!iter->status().ok()) { + result = iter->status().ToString(); + } else { + result = "[ "; + bool first = true; + while (iter->Valid()) { + ParsedInternalKey ikey; + if (!ParseInternalKey(iter->key(), &ikey)) { + result += "CORRUPTED"; + } else { + if (last_options_.comparator->Compare(ikey.user_key, user_key) != 0) { + break; + } + if (!first) { + result += ", "; + } + first = false; + switch (ikey.type) { + case kTypeValue: + result += iter->value().ToString(); + break; + case kTypeDeletion: + result += "DEL"; + break; + } + } + iter->Next(); + } + if (!first) { + result += " "; + } + result += "]"; + } + delete iter; + return result; + } + + int NumTableFilesAtLevel(int level) { + std::string property; + ASSERT_TRUE( + db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level), + &property)); + return atoi(property.c_str()); + } + + int TotalTableFiles() { + int result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + result += NumTableFilesAtLevel(level); + } + return result; + } + + // Return spread of files per level + std::string FilesPerLevel() { + std::string result; + int last_non_zero_offset = 0; + for (int level = 0; level < config::kNumLevels; level++) { + int f = NumTableFilesAtLevel(level); + char buf[100]; + snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f); + result += buf; + if (f > 0) { + last_non_zero_offset = result.size(); + } + } + result.resize(last_non_zero_offset); + return result; + } + + int CountFiles() { + std::vector<std::string> files; + env_->GetChildren(dbname_, &files); + return static_cast<int>(files.size()); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void Compact(const Slice& start, const Slice& limit) { + db_->CompactRange(&start, &limit); + } + + // Do n memtable compactions, each of which produces an sstable + // covering the range [small,large]. + void MakeTables(int n, const std::string& small, const std::string& large) { + for (int i = 0; i < n; i++) { + Put(small, "begin"); + Put(large, "end"); + dbfull()->TEST_CompactMemTable(); + } + } + + // Prevent pushing of new sstables into deeper levels by adding + // tables that cover a specified range to all levels. + void FillLevels(const std::string& smallest, const std::string& largest) { + MakeTables(config::kNumLevels, smallest, largest); + } + + void DumpFileCounts(const char* label) { + fprintf(stderr, "---\n%s:\n", label); + fprintf(stderr, "maxoverlap: %lld\n", + static_cast<long long>( + dbfull()->TEST_MaxNextLevelOverlappingBytes())); + for (int level = 0; level < config::kNumLevels; level++) { + int num = NumTableFilesAtLevel(level); + if (num > 0) { + fprintf(stderr, " level %3d : %d files\n", level, num); + } + } + } + + std::string DumpSSTableList() { + std::string property; + db_->GetProperty("leveldb.sstables", &property); + return property; + } + + std::string IterStatus(Iterator* iter) { + std::string result; + if (iter->Valid()) { + result = iter->key().ToString() + "->" + iter->value().ToString(); + } else { + result = "(invalid)"; + } + return result; + } +}; + +TEST(DBTest, Empty) { + do { + ASSERT_TRUE(db_ != NULL); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, ReadWrite) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + ASSERT_EQ("v3", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + } while (ChangeOptions()); +} + +TEST(DBTest, PutDeleteGet) { + do { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + ASSERT_OK(db_->Delete(WriteOptions(), "foo")); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromImmutableLayer) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + + env_->delay_sstable_sync_.Release_Store(env_); // Block sync calls + Put("k1", std::string(100000, 'x')); // Fill memtable + Put("k2", std::string(100000, 'y')); // Trigger compaction + ASSERT_EQ("v1", Get("foo")); + env_->delay_sstable_sync_.Release_Store(NULL); // Release sync calls + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromVersions) { + do { + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v1", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetSnapshot) { + do { + // Try with both a short key and a long key + for (int i = 0; i < 2; i++) { + std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x'); + ASSERT_OK(Put(key, "v1")); + const Snapshot* s1 = db_->GetSnapshot(); + ASSERT_OK(Put(key, "v2")); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + db_->ReleaseSnapshot(s1); + } + } while (ChangeOptions()); +} + +TEST(DBTest, GetLevel0Ordering) { + do { + // Check that we process level-0 files in correct order. The code + // below generates two level-0 files where the earlier one comes + // before the later one in the level-0 file list since the earlier + // one has a smaller "smallest" key. + ASSERT_OK(Put("bar", "b")); + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("foo", "v2")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetOrderedByLevels) { + do { + ASSERT_OK(Put("foo", "v1")); + Compact("a", "z"); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetPicksCorrectFile) { + do { + // Arrange to have multiple files in a non-level-0 level. + ASSERT_OK(Put("a", "va")); + Compact("a", "b"); + ASSERT_OK(Put("x", "vx")); + Compact("x", "y"); + ASSERT_OK(Put("f", "vf")); + Compact("f", "g"); + ASSERT_EQ("va", Get("a")); + ASSERT_EQ("vf", Get("f")); + ASSERT_EQ("vx", Get("x")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetEncountersEmptyLevel) { + do { + // Arrange for the following to happen: + // * sstable A in level 0 + // * nothing in level 1 + // * sstable B in level 2 + // Then do enough Get() calls to arrange for an automatic compaction + // of sstable A. A bug would cause the compaction to be marked as + // occuring at level 1 (instead of the correct level 0). + + // Step 1: First place sstables in levels 0 and 2 + int compaction_count = 0; + while (NumTableFilesAtLevel(0) == 0 || + NumTableFilesAtLevel(2) == 0) { + ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2"; + compaction_count++; + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + } + + // Step 2: clear level 1 if necessary. + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_EQ(NumTableFilesAtLevel(0), 1); + ASSERT_EQ(NumTableFilesAtLevel(1), 0); + ASSERT_EQ(NumTableFilesAtLevel(2), 1); + + // Step 3: read a bunch of times + for (int i = 0; i < 1000; i++) { + ASSERT_EQ("NOT_FOUND", Get("missing")); + } + + // Step 4: Wait for compaction to finish + env_->SleepForMicroseconds(1000000); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } while (ChangeOptions()); +} + +TEST(DBTest, IterEmpty) { + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("foo"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSingle) { + ASSERT_OK(Put("a", "va")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMulti) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("ax"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("z"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + // Switch from reverse to forward + iter->SeekToLast(); + iter->Prev(); + iter->Prev(); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Switch from forward to reverse + iter->SeekToFirst(); + iter->Next(); + iter->Next(); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Make sure iter stays at snapshot + ASSERT_OK(Put("a", "va2")); + ASSERT_OK(Put("a2", "va3")); + ASSERT_OK(Put("b", "vb2")); + ASSERT_OK(Put("c", "vc2")); + ASSERT_OK(Delete("b")); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSmallAndLargeMix) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", std::string(100000, 'b'))); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Put("d", std::string(100000, 'd'))); + ASSERT_OK(Put("e", std::string(100000, 'e'))); + + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMultiWithDelete) { + do { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Delete("b")); + ASSERT_EQ("NOT_FOUND", Get("b")); + + Iterator* iter = db_->NewIterator(ReadOptions()); + iter->Seek("c"); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + delete iter; + } while (ChangeOptions()); +} + +TEST(DBTest, Recover) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("baz", "v5")); + + Reopen(); + ASSERT_EQ("v1", Get("foo")); + + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v5", Get("baz")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + + Reopen(); + ASSERT_EQ("v3", Get("foo")); + ASSERT_OK(Put("foo", "v4")); + ASSERT_EQ("v4", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ("v5", Get("baz")); + } while (ChangeOptions()); +} + +TEST(DBTest, RecoveryWithEmptyLog) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("foo", "v2")); + Reopen(); + Reopen(); + ASSERT_OK(Put("foo", "v3")); + Reopen(); + ASSERT_EQ("v3", Get("foo")); + } while (ChangeOptions()); +} + +// Check that writes done during a memtable compaction are recovered +// if the database is shutdown during the memtable compaction. +TEST(DBTest, RecoverDuringMemtableCompaction) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 1000000; + Reopen(&options); + + // Trigger a long memtable compaction and reopen the database during it + ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file + ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable + ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction + ASSERT_OK(Put("bar", "v2")); // Goes to new log file + + Reopen(&options); + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ(std::string(10000000, 'x'), Get("big1")); + ASSERT_EQ(std::string(1000, 'y'), Get("big2")); + } while (ChangeOptions()); +} + +static std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); +} + +TEST(DBTest, MinorCompactionsHappen) { + Options options = CurrentOptions(); + options.write_buffer_size = 10000; + Reopen(&options); + + const int N = 500; + + int starting_num_tables = TotalTableFiles(); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v'))); + } + int ending_num_tables = TotalTableFiles(); + ASSERT_GT(ending_num_tables, starting_num_tables); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } + + Reopen(); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } +} + +TEST(DBTest, RecoverWithLargeLog) { + { + Options options = CurrentOptions(); + Reopen(&options); + ASSERT_OK(Put("big1", std::string(200000, '1'))); + ASSERT_OK(Put("big2", std::string(200000, '2'))); + ASSERT_OK(Put("small3", std::string(10, '3'))); + ASSERT_OK(Put("small4", std::string(10, '4'))); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } + + // Make sure that if we re-open with a small write buffer size that + // we flush table files in the middle of a large log file. + Options options = CurrentOptions(); + options.write_buffer_size = 100000; + Reopen(&options); + ASSERT_EQ(NumTableFilesAtLevel(0), 3); + ASSERT_EQ(std::string(200000, '1'), Get("big1")); + ASSERT_EQ(std::string(200000, '2'), Get("big2")); + ASSERT_EQ(std::string(10, '3'), Get("small3")); + ASSERT_EQ(std::string(10, '4'), Get("small4")); + ASSERT_GT(NumTableFilesAtLevel(0), 1); +} + +TEST(DBTest, CompactionsGenerateMultipleFiles) { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + Reopen(&options); + + Random rnd(301); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + std::vector<std::string> values; + for (int i = 0; i < 80; i++) { + values.push_back(RandomString(&rnd, 100000)); + ASSERT_OK(Put(Key(i), values[i])); + } + + // Reopening moves updates to level-0 + Reopen(&options); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 1); + for (int i = 0; i < 80; i++) { + ASSERT_EQ(Get(Key(i)), values[i]); + } +} + +TEST(DBTest, RepeatedWritesToSameKey) { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + // We must have at most one file per level except for level-0, + // which may have up to kL0_StopWritesTrigger files. + const int kMaxFiles = config::kNumLevels + config::kL0_StopWritesTrigger; + + Random rnd(301); + std::string value = RandomString(&rnd, 2 * options.write_buffer_size); + for (int i = 0; i < 5 * kMaxFiles; i++) { + Put("key", value); + ASSERT_LE(TotalTableFiles(), kMaxFiles); + fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles()); + } +} + +TEST(DBTest, SparseMerge) { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(&options); + + FillLevels("A", "Z"); + + // Suppose there is: + // small amount of data with prefix A + // large amount of data with prefix B + // small amount of data with prefix C + // and that recent updates have made small changes to all three prefixes. + // Check that we do not do a compaction that merges all of B in one shot. + const std::string value(1000, 'x'); + Put("A", "va"); + // Write approximately 100MB of "B" values + for (int i = 0; i < 100000; i++) { + char key[100]; + snprintf(key, sizeof(key), "B%010d", i); + Put(key, value); + } + Put("C", "vc"); + dbfull()->TEST_CompactMemTable(); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + // Make sparse update + Put("A", "va2"); + Put("B100", "bvalue2"); + Put("C", "vc2"); + dbfull()->TEST_CompactMemTable(); + + // Compactions should not cause us to create a situation where + // a file overlaps too much data at the next level. + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(0, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +TEST(DBTest, ApproximateSizes) { + do { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + options.compression = kNoCompression; + DestroyAndReopen(); + + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + Reopen(&options); + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + const int N = 80; + static const int S1 = 100000; + static const int S2 = 105000; // Allow some expansion from metadata + Random rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), RandomString(&rnd, S1))); + } + + // 0 because GetApproximateSizes() does not account for memtable space + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + for (int compact_start = 0; compact_start < N; compact_start += 10) { + for (int i = 0; i < N; i += 10) { + ASSERT_TRUE(Between(Size("", Key(i)), S1*i, S2*i)); + ASSERT_TRUE(Between(Size("", Key(i)+".suffix"), S1*(i+1), S2*(i+1))); + ASSERT_TRUE(Between(Size(Key(i), Key(i+10)), S1*10, S2*10)); + } + ASSERT_TRUE(Between(Size("", Key(50)), S1*50, S2*50)); + ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), S1*50, S2*50)); + + std::string cstart_str = Key(compact_start); + std::string cend_str = Key(compact_start + 9); + Slice cstart = cstart_str; + Slice cend = cend_str; + dbfull()->TEST_CompactRange(0, &cstart, &cend); + } + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 0); + } + } while (ChangeOptions()); +} + +TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { + do { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(); + + Random rnd(301); + std::string big1 = RandomString(&rnd, 100000); + ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(2), big1)); + ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(4), big1)); + ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000))); + ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000))); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + ASSERT_TRUE(Between(Size("", Key(0)), 0, 0)); + ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000)); + ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000)); + ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000)); + ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000)); + ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000)); + ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000)); + ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000)); + ASSERT_TRUE(Between(Size("", Key(8)), 550000, 560000)); + + ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000)); + + dbfull()->TEST_CompactRange(0, NULL, NULL); + } + } while (ChangeOptions()); +} + +TEST(DBTest, IteratorPinsRef) { + Put("foo", "hello"); + + // Get iterator that will yield the current contents of the DB. + Iterator* iter = db_->NewIterator(ReadOptions()); + + // Write to force compactions + Put("foo", "newvalue1"); + for (int i = 0; i < 100; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values + } + Put("foo", "newvalue2"); + + iter->SeekToFirst(); + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ("foo", iter->key().ToString()); + ASSERT_EQ("hello", iter->value().ToString()); + iter->Next(); + ASSERT_TRUE(!iter->Valid()); + delete iter; +} + +TEST(DBTest, Snapshot) { + do { + Put("foo", "v1"); + const Snapshot* s1 = db_->GetSnapshot(); + Put("foo", "v2"); + const Snapshot* s2 = db_->GetSnapshot(); + Put("foo", "v3"); + const Snapshot* s3 = db_->GetSnapshot(); + + Put("foo", "v4"); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v3", Get("foo", s3)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s3); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s1); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s2); + ASSERT_EQ("v4", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, HiddenValuesAreRemoved) { + do { + Random rnd(301); + FillLevels("a", "z"); + + std::string big = RandomString(&rnd, 50000); + Put("foo", big); + Put("pastfoo", "v"); + const Snapshot* snapshot = db_->GetSnapshot(); + Put("foo", "tiny"); + Put("pastfoo2", "v2"); // Advance sequence number one more + + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + ASSERT_GT(NumTableFilesAtLevel(0), 0); + + ASSERT_EQ(big, Get("foo", snapshot)); + ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000)); + db_->ReleaseSnapshot(snapshot); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]"); + Slice x("x"); + dbfull()->TEST_CompactRange(0, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GE(NumTableFilesAtLevel(1), 1); + dbfull()->TEST_CompactRange(1, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + + ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000)); + } while (ChangeOptions()); +} + +TEST(DBTest, DeletionMarkers1) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + Put("foo", "v2"); + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + Slice z("z"); + dbfull()->TEST_CompactRange(last-2, NULL, &z); + // DEL eliminated, but v1 remains because we aren't compacting that level + // (DEL can be eliminated because v2 hides v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]"); +} + +TEST(DBTest, DeletionMarkers2) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-2, NULL, NULL); + // DEL kept: "last" file overlaps + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ ]"); +} + +TEST(DBTest, OverlapInLevel0) { + do { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config"; + + // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0. + ASSERT_OK(Put("100", "v100")); + ASSERT_OK(Put("999", "v999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Delete("100")); + ASSERT_OK(Delete("999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("0,1,1", FilesPerLevel()); + + // Make files spanning the following ranges in level-0: + // files[0] 200 .. 900 + // files[1] 300 .. 500 + // Note that files are sorted by smallest key. + ASSERT_OK(Put("300", "v300")); + ASSERT_OK(Put("500", "v500")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("200", "v200")); + ASSERT_OK(Put("600", "v600")); + ASSERT_OK(Put("900", "v900")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("2,1,1", FilesPerLevel()); + + // Compact away the placeholder files we created initially + dbfull()->TEST_CompactRange(1, NULL, NULL); + dbfull()->TEST_CompactRange(2, NULL, NULL); + ASSERT_EQ("2", FilesPerLevel()); + + // Do a memtable compaction. Before bug-fix, the compaction would + // not detect the overlap with level-0 files and would incorrectly place + // the deletion in a deeper level. + ASSERT_OK(Delete("600")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("3", FilesPerLevel()); + ASSERT_EQ("NOT_FOUND", Get("600")); + } while (ChangeOptions()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_a) { + Reopen(); + ASSERT_OK(Put("b", "v")); + Reopen(); + ASSERT_OK(Delete("b")); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Put("a", "v")); + Reopen(); + Reopen(); + ASSERT_EQ("(a->v)", Contents()); + env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + ASSERT_EQ("(a->v)", Contents()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_b) { + Reopen(); + Put("",""); + Reopen(); + Delete("e"); + Put("",""); + Reopen(); + Put("c", "cv"); + Reopen(); + Put("",""); + Reopen(); + Put("",""); + env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + Reopen(); + Put("d","dv"); + Reopen(); + Put("",""); + Reopen(); + Delete("d"); + Delete("b"); + Reopen(); + ASSERT_EQ("(->)(c->cv)", Contents()); + env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + ASSERT_EQ("(->)(c->cv)", Contents()); +} + +TEST(DBTest, ComparatorCheck) { + class NewComparator : public Comparator { + public: + virtual const char* Name() const { return "leveldb.NewComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(a, b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + BytewiseComparator()->FindShortestSeparator(s, l); + } + virtual void FindShortSuccessor(std::string* key) const { + BytewiseComparator()->FindShortSuccessor(key); + } + }; + NewComparator cmp; + Options new_options = CurrentOptions(); + new_options.comparator = &cmp; + Status s = TryReopen(&new_options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("comparator") != std::string::npos) + << s.ToString(); +} + +TEST(DBTest, CustomComparator) { + class NumberComparator : public Comparator { + public: + virtual const char* Name() const { return "test.NumberComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return ToNumber(a) - ToNumber(b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + ToNumber(*s); // Check format + ToNumber(l); // Check format + } + virtual void FindShortSuccessor(std::string* key) const { + ToNumber(*key); // Check format + } + private: + static int ToNumber(const Slice& x) { + // Check that there are no extra characters. + ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size()-1] == ']') + << EscapeString(x); + int val; + char ignored; + ASSERT_TRUE(sscanf(x.ToString().c_str(), "[%i]%c", &val, &ignored) == 1) + << EscapeString(x); + return val; + } + }; + NumberComparator cmp; + Options new_options = CurrentOptions(); + new_options.create_if_missing = true; + new_options.comparator = &cmp; + new_options.filter_policy = NULL; // Cannot use bloom filters + new_options.write_buffer_size = 1000; // Compact more often + DestroyAndReopen(&new_options); + ASSERT_OK(Put("[10]", "ten")); + ASSERT_OK(Put("[0x14]", "twenty")); + for (int i = 0; i < 2; i++) { + ASSERT_EQ("ten", Get("[10]")); + ASSERT_EQ("ten", Get("[0xa]")); + ASSERT_EQ("twenty", Get("[20]")); + ASSERT_EQ("twenty", Get("[0x14]")); + ASSERT_EQ("NOT_FOUND", Get("[15]")); + ASSERT_EQ("NOT_FOUND", Get("[0xf]")); + Compact("[0]", "[9999]"); + } + + for (int run = 0; run < 2; run++) { + for (int i = 0; i < 1000; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "[%d]", i*10); + ASSERT_OK(Put(buf, buf)); + } + Compact("[0]", "[1000000]"); + } +} + +TEST(DBTest, ManualCompaction) { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) + << "Need to update this test to match kMaxMemCompactLevel"; + + MakeTables(3, "p", "q"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls before files + Compact("", "c"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls after files + Compact("r", "z"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range overlaps files + Compact("p1", "p9"); + ASSERT_EQ("0,0,1", FilesPerLevel()); + + // Populate a different range + MakeTables(3, "c", "e"); + ASSERT_EQ("1,1,2", FilesPerLevel()); + + // Compact just the new range + Compact("b", "f"); + ASSERT_EQ("0,0,2", FilesPerLevel()); + + // Compact all + MakeTables(1, "a", "z"); + ASSERT_EQ("0,1,2", FilesPerLevel()); + db_->CompactRange(NULL, NULL); + ASSERT_EQ("0,0,1", FilesPerLevel()); +} + +TEST(DBTest, DBOpen_Options) { + std::string dbname = test::TmpDir() + "/db_options_test"; + DestroyDB(dbname, Options()); + + // Does not exist, and create_if_missing == false: error + DB* db = NULL; + Options opts; + opts.create_if_missing = false; + Status s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL); + ASSERT_TRUE(db == NULL); + + // Does not exist, and create_if_missing == true: OK + opts.create_if_missing = true; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + // Does exist, and error_if_exists == true: error + opts.create_if_missing = false; + opts.error_if_exists = true; + s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL); + ASSERT_TRUE(db == NULL); + + // Does exist, and error_if_exists == false: OK + opts.create_if_missing = true; + opts.error_if_exists = false; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; +} + +// Check that number of files does not grow when we are out of space +TEST(DBTest, NoSpace) { + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + Compact("a", "z"); + const int num_files = CountFiles(); + env_->no_space_.Release_Store(env_); // Force out-of-space errors + env_->sleep_counter_.Reset(); + for (int i = 0; i < 5; i++) { + for (int level = 0; level < config::kNumLevels-1; level++) { + dbfull()->TEST_CompactRange(level, NULL, NULL); + } + } + env_->no_space_.Release_Store(NULL); + ASSERT_LT(CountFiles(), num_files + 3); + + // Check that compaction attempts slept after errors + ASSERT_GE(env_->sleep_counter_.Read(), 5); +} + +TEST(DBTest, NonWritableFileSystem) { + Options options = CurrentOptions(); + options.write_buffer_size = 1000; + options.env = env_; + Reopen(&options); + ASSERT_OK(Put("foo", "v1")); + env_->non_writable_.Release_Store(env_); // Force errors for new files + std::string big(100000, 'x'); + int errors = 0; + for (int i = 0; i < 20; i++) { + fprintf(stderr, "iter %d; errors %d\n", i, errors); + if (!Put("foo", big).ok()) { + errors++; + env_->SleepForMicroseconds(100000); + } + } + ASSERT_GT(errors, 0); + env_->non_writable_.Release_Store(NULL); +} + +TEST(DBTest, FilesDeletedAfterCompaction) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + const int num_files = CountFiles(); + for (int i = 0; i < 10; i++) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + } + ASSERT_EQ(CountFiles(), num_files); +} + +TEST(DBTest, BloomFilter) { + env_->count_random_reads_ = true; + Options options = CurrentOptions(); + options.env = env_; + options.block_cache = NewLRUCache(0); // Prevent cache hits + options.filter_policy = NewBloomFilterPolicy(10); + Reopen(&options); + + // Populate multiple layers + const int N = 10000; + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i))); + } + Compact("a", "z"); + for (int i = 0; i < N; i += 100) { + ASSERT_OK(Put(Key(i), Key(i))); + } + dbfull()->TEST_CompactMemTable(); + + // Prevent auto compactions triggered by seeks + env_->delay_sstable_sync_.Release_Store(env_); + + // Lookup present keys. Should rarely read from small sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i), Get(Key(i))); + } + int reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d present => %d reads\n", N, reads); + ASSERT_GE(reads, N); + ASSERT_LE(reads, N + 2*N/100); + + // Lookup present keys. Should rarely read from either sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ("NOT_FOUND", Get(Key(i) + ".missing")); + } + reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d missing => %d reads\n", N, reads); + ASSERT_LE(reads, 3*N/100); + + env_->delay_sstable_sync_.Release_Store(NULL); + Close(); + delete options.block_cache; + delete options.filter_policy; +} + +// Multi-threaded test: +namespace { + +static const int kNumThreads = 4; +static const int kTestSeconds = 10; +static const int kNumKeys = 1000; + +struct MTState { + DBTest* test; + port::AtomicPointer stop; + port::AtomicPointer counter[kNumThreads]; + port::AtomicPointer thread_done[kNumThreads]; +}; + +struct MTThread { + MTState* state; + int id; +}; + +static void MTThreadBody(void* arg) { + MTThread* t = reinterpret_cast<MTThread*>(arg); + int id = t->id; + DB* db = t->state->test->db_; + uintptr_t counter = 0; + fprintf(stderr, "... starting thread %d\n", id); + Random rnd(1000 + id); + std::string value; + char valbuf[1500]; + while (t->state->stop.Acquire_Load() == NULL) { + t->state->counter[id].Release_Store(reinterpret_cast<void*>(counter)); + + int key = rnd.Uniform(kNumKeys); + char keybuf[20]; + snprintf(keybuf, sizeof(keybuf), "%016d", key); + + if (rnd.OneIn(2)) { + // Write values of the form <key, my id, counter>. + // We add some padding for force compactions. + snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", + key, id, static_cast<int>(counter)); + ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf))); + } else { + // Read a value and verify that it matches the pattern written above. + Status s = db->Get(ReadOptions(), Slice(keybuf), &value); + if (s.IsNotFound()) { + // Key has not yet been written + } else { + // Check that the writer thread counter is >= the counter in the value + ASSERT_OK(s); + int k, w, c; + ASSERT_EQ(3, sscanf(value.c_str(), "%d.%d.%d", &k, &w, &c)) << value; + ASSERT_EQ(k, key); + ASSERT_GE(w, 0); + ASSERT_LT(w, kNumThreads); + ASSERT_LE(c, reinterpret_cast<uintptr_t>( + t->state->counter[w].Acquire_Load())); + } + } + counter++; + } + t->state->thread_done[id].Release_Store(t); + fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter)); +} + +} // namespace + +TEST(DBTest, MultiThreaded) { + do { + // Initialize state + MTState mt; + mt.test = this; + mt.stop.Release_Store(0); + for (int id = 0; id < kNumThreads; id++) { + mt.counter[id].Release_Store(0); + mt.thread_done[id].Release_Store(0); + } + + // Start threads + MTThread thread[kNumThreads]; + for (int id = 0; id < kNumThreads; id++) { + thread[id].state = &mt; + thread[id].id = id; + env_->StartThread(MTThreadBody, &thread[id]); + } + + // Let them run for a while + env_->SleepForMicroseconds(kTestSeconds * 1000000); + + // Stop the threads and wait for them to finish + mt.stop.Release_Store(&mt); + for (int id = 0; id < kNumThreads; id++) { + while (mt.thread_done[id].Acquire_Load() == NULL) { + env_->SleepForMicroseconds(100000); + } + } + } while (ChangeOptions()); +} + +namespace { +typedef std::map<std::string, std::string> KVMap; +} + +class ModelDB: public DB { + public: + class ModelSnapshot : public Snapshot { + public: + KVMap map_; + }; + + explicit ModelDB(const Options& options): options_(options) { } + ~ModelDB() { } + virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { + return DB::Put(o, k, v); + } + virtual Status Delete(const WriteOptions& o, const Slice& key) { + return DB::Delete(o, key); + } + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) { + assert(false); // Not implemented + return Status::NotFound(key); + } + virtual Iterator* NewIterator(const ReadOptions& options) { + if (options.snapshot == NULL) { + KVMap* saved = new KVMap; + *saved = map_; + return new ModelIter(saved, true); + } else { + const KVMap* snapshot_state = + &(reinterpret_cast<const ModelSnapshot*>(options.snapshot)->map_); + return new ModelIter(snapshot_state, false); + } + } + virtual const Snapshot* GetSnapshot() { + ModelSnapshot* snapshot = new ModelSnapshot; + snapshot->map_ = map_; + return snapshot; + } + + virtual void ReleaseSnapshot(const Snapshot* snapshot) { + delete reinterpret_cast<const ModelSnapshot*>(snapshot); + } + virtual Status Write(const WriteOptions& options, WriteBatch* batch) { + class Handler : public WriteBatch::Handler { + public: + KVMap* map_; + virtual void Put(const Slice& key, const Slice& value) { + (*map_)[key.ToString()] = value.ToString(); + } + virtual void Delete(const Slice& key) { + map_->erase(key.ToString()); + } + }; + Handler handler; + handler.map_ = &map_; + return batch->Iterate(&handler); + } + + virtual bool GetProperty(const Slice& property, std::string* value) { + return false; + } + virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) { + for (int i = 0; i < n; i++) { + sizes[i] = 0; + } + } + virtual void CompactRange(const Slice* start, const Slice* end) { + } + + private: + class ModelIter: public Iterator { + public: + ModelIter(const KVMap* map, bool owned) + : map_(map), owned_(owned), iter_(map_->end()) { + } + ~ModelIter() { + if (owned_) delete map_; + } + virtual bool Valid() const { return iter_ != map_->end(); } + virtual void SeekToFirst() { iter_ = map_->begin(); } + virtual void SeekToLast() { + if (map_->empty()) { + iter_ = map_->end(); + } else { + iter_ = map_->find(map_->rbegin()->first); + } + } + virtual void Seek(const Slice& k) { + iter_ = map_->lower_bound(k.ToString()); + } + virtual void Next() { ++iter_; } + virtual void Prev() { --iter_; } + virtual Slice key() const { return iter_->first; } + virtual Slice value() const { return iter_->second; } + virtual Status status() const { return Status::OK(); } + private: + const KVMap* const map_; + const bool owned_; // Do we own map_ + KVMap::const_iterator iter_; + }; + const Options options_; + KVMap map_; +}; + +static std::string RandomKey(Random* rnd) { + int len = (rnd->OneIn(3) + ? 1 // Short sometimes to encourage collisions + : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); + return test::RandomKey(rnd, len); +} + +static bool CompareIterators(int step, + DB* model, + DB* db, + const Snapshot* model_snap, + const Snapshot* db_snap) { + ReadOptions options; + options.snapshot = model_snap; + Iterator* miter = model->NewIterator(options); + options.snapshot = db_snap; + Iterator* dbiter = db->NewIterator(options); + bool ok = true; + int count = 0; + for (miter->SeekToFirst(), dbiter->SeekToFirst(); + ok && miter->Valid() && dbiter->Valid(); + miter->Next(), dbiter->Next()) { + count++; + if (miter->key().compare(dbiter->key()) != 0) { + fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(dbiter->key()).c_str()); + ok = false; + break; + } + + if (miter->value().compare(dbiter->value()) != 0) { + fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(miter->value()).c_str(), + EscapeString(miter->value()).c_str()); + ok = false; + } + } + + if (ok) { + if (miter->Valid() != dbiter->Valid()) { + fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n", + step, miter->Valid(), dbiter->Valid()); + ok = false; + } + } + fprintf(stderr, "%d entries compared: ok=%d\n", count, ok); + delete miter; + delete dbiter; + return ok; +} + +TEST(DBTest, Randomized) { + Random rnd(test::RandomSeed()); + do { + ModelDB model(CurrentOptions()); + const int N = 10000; + const Snapshot* model_snap = NULL; + const Snapshot* db_snap = NULL; + std::string k, v; + for (int step = 0; step < N; step++) { + if (step % 100 == 0) { + fprintf(stderr, "Step %d of %d\n", step, N); + } + // TODO(sanjay): Test Get() works + int p = rnd.Uniform(100); + if (p < 45) { // Put + k = RandomKey(&rnd); + v = RandomString(&rnd, + rnd.OneIn(20) + ? 100 + rnd.Uniform(100) + : rnd.Uniform(8)); + ASSERT_OK(model.Put(WriteOptions(), k, v)); + ASSERT_OK(db_->Put(WriteOptions(), k, v)); + + } else if (p < 90) { // Delete + k = RandomKey(&rnd); + ASSERT_OK(model.Delete(WriteOptions(), k)); + ASSERT_OK(db_->Delete(WriteOptions(), k)); + + + } else { // Multi-element batch + WriteBatch b; + const int num = rnd.Uniform(8); + for (int i = 0; i < num; i++) { + if (i == 0 || !rnd.OneIn(10)) { + k = RandomKey(&rnd); + } else { + // Periodically re-use the same key from the previous iter, so + // we have multiple entries in the write batch for the same key + } + if (rnd.OneIn(2)) { + v = RandomString(&rnd, rnd.Uniform(10)); + b.Put(k, v); + } else { + b.Delete(k); + } + } + ASSERT_OK(model.Write(WriteOptions(), &b)); + ASSERT_OK(db_->Write(WriteOptions(), &b)); + } + + if ((step % 100) == 0) { + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap)); + // Save a snapshot from each DB this time that we'll use next + // time we compare things, to make sure the current state is + // preserved with the snapshot + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + + Reopen(); + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + + model_snap = model.GetSnapshot(); + db_snap = db_->GetSnapshot(); + } + } + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + } while (ChangeOptions()); +} + +std::string MakeKey(unsigned int num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%016u", num); + return std::string(buf); +} + +void BM_LogAndApply(int iters, int num_base_files) { + std::string dbname = test::TmpDir() + "/leveldb_test_benchmark"; + DestroyDB(dbname, Options()); + + DB* db = NULL; + Options opts; + opts.create_if_missing = true; + Status s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + Env* env = Env::Default(); + + port::Mutex mu; + MutexLock l(&mu); + + InternalKeyComparator cmp(BytewiseComparator()); + Options options; + VersionSet vset(dbname, &options, NULL, &cmp); + ASSERT_OK(vset.Recover()); + VersionEdit vbase; + uint64_t fnum = 1; + for (int i = 0; i < num_base_files; i++) { + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vbase.AddFile(2, fnum++, 1 /* file size */, start, limit); + } + ASSERT_OK(vset.LogAndApply(&vbase, &mu)); + + uint64_t start_micros = env->NowMicros(); + + for (int i = 0; i < iters; i++) { + VersionEdit vedit; + vedit.DeleteFile(2, fnum); + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vedit.AddFile(2, fnum++, 1 /* file size */, start, limit); + vset.LogAndApply(&vedit, &mu); + } + uint64_t stop_micros = env->NowMicros(); + unsigned int us = stop_micros - start_micros; + char buf[16]; + snprintf(buf, sizeof(buf), "%d", num_base_files); + fprintf(stderr, + "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", + buf, iters, us, ((float)us) / iters); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + if (argc > 1 && std::string(argv[1]) == "--benchmark") { + leveldb::BM_LogAndApply(1000, 1); + leveldb::BM_LogAndApply(1000, 100); + leveldb::BM_LogAndApply(1000, 10000); + leveldb::BM_LogAndApply(100, 100000); + return 0; + } + + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/dbformat.cc b/src/leveldb/db/dbformat.cc new file mode 100644 index 0000000000..28e11b398d --- /dev/null +++ b/src/leveldb/db/dbformat.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <stdio.h> +#include "db/dbformat.h" +#include "port/port.h" +#include "util/coding.h" + +namespace leveldb { + +static uint64_t PackSequenceAndType(uint64_t seq, ValueType t) { + assert(seq <= kMaxSequenceNumber); + assert(t <= kValueTypeForSeek); + return (seq << 8) | t; +} + +void AppendInternalKey(std::string* result, const ParsedInternalKey& key) { + result->append(key.user_key.data(), key.user_key.size()); + PutFixed64(result, PackSequenceAndType(key.sequence, key.type)); +} + +std::string ParsedInternalKey::DebugString() const { + char buf[50]; + snprintf(buf, sizeof(buf), "' @ %llu : %d", + (unsigned long long) sequence, + int(type)); + std::string result = "'"; + result += user_key.ToString(); + result += buf; + return result; +} + +std::string InternalKey::DebugString() const { + std::string result; + ParsedInternalKey parsed; + if (ParseInternalKey(rep_, &parsed)) { + result = parsed.DebugString(); + } else { + result = "(bad)"; + result.append(EscapeString(rep_)); + } + return result; +} + +const char* InternalKeyComparator::Name() const { + return "leveldb.InternalKeyComparator"; +} + +int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const { + // Order by: + // increasing user key (according to user-supplied comparator) + // decreasing sequence number + // decreasing type (though sequence# should be enough to disambiguate) + int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey)); + if (r == 0) { + const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8); + const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8); + if (anum > bnum) { + r = -1; + } else if (anum < bnum) { + r = +1; + } + } + return r; +} + +void InternalKeyComparator::FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Attempt to shorten the user portion of the key + Slice user_start = ExtractUserKey(*start); + Slice user_limit = ExtractUserKey(limit); + std::string tmp(user_start.data(), user_start.size()); + user_comparator_->FindShortestSeparator(&tmp, user_limit); + if (tmp.size() < user_start.size() && + user_comparator_->Compare(user_start, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*start, tmp) < 0); + assert(this->Compare(tmp, limit) < 0); + start->swap(tmp); + } +} + +void InternalKeyComparator::FindShortSuccessor(std::string* key) const { + Slice user_key = ExtractUserKey(*key); + std::string tmp(user_key.data(), user_key.size()); + user_comparator_->FindShortSuccessor(&tmp); + if (tmp.size() < user_key.size() && + user_comparator_->Compare(user_key, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*key, tmp) < 0); + key->swap(tmp); + } +} + +const char* InternalFilterPolicy::Name() const { + return user_policy_->Name(); +} + +void InternalFilterPolicy::CreateFilter(const Slice* keys, int n, + std::string* dst) const { + // We rely on the fact that the code in table.cc does not mind us + // adjusting keys[]. + Slice* mkey = const_cast<Slice*>(keys); + for (int i = 0; i < n; i++) { + mkey[i] = ExtractUserKey(keys[i]); + // TODO(sanjay): Suppress dups? + } + user_policy_->CreateFilter(keys, n, dst); +} + +bool InternalFilterPolicy::KeyMayMatch(const Slice& key, const Slice& f) const { + return user_policy_->KeyMayMatch(ExtractUserKey(key), f); +} + +LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) { + size_t usize = user_key.size(); + size_t needed = usize + 13; // A conservative estimate + char* dst; + if (needed <= sizeof(space_)) { + dst = space_; + } else { + dst = new char[needed]; + } + start_ = dst; + dst = EncodeVarint32(dst, usize + 8); + kstart_ = dst; + memcpy(dst, user_key.data(), usize); + dst += usize; + EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek)); + dst += 8; + end_ = dst; +} + +} // namespace leveldb diff --git a/src/leveldb/db/dbformat.h b/src/leveldb/db/dbformat.h new file mode 100644 index 0000000000..f7f64dafb6 --- /dev/null +++ b/src/leveldb/db/dbformat.h @@ -0,0 +1,227 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_FORMAT_H_ +#define STORAGE_LEVELDB_DB_FORMAT_H_ + +#include <stdio.h> +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "leveldb/slice.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +// Grouping of constants. We may want to make some of these +// parameters set via options. +namespace config { +static const int kNumLevels = 7; + +// Level-0 compaction is started when we hit this many files. +static const int kL0_CompactionTrigger = 4; + +// Soft limit on number of level-0 files. We slow down writes at this point. +static const int kL0_SlowdownWritesTrigger = 8; + +// Maximum number of level-0 files. We stop writes at this point. +static const int kL0_StopWritesTrigger = 12; + +// Maximum level to which a new compacted memtable is pushed if it +// does not create overlap. We try to push to level 2 to avoid the +// relatively expensive level 0=>1 compactions and to avoid some +// expensive manifest file operations. We do not push all the way to +// the largest level since that can generate a lot of wasted disk +// space if the same key space is being repeatedly overwritten. +static const int kMaxMemCompactLevel = 2; + +} // namespace config + +class InternalKey; + +// Value types encoded as the last component of internal keys. +// DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk +// data structures. +enum ValueType { + kTypeDeletion = 0x0, + kTypeValue = 0x1 +}; +// kValueTypeForSeek defines the ValueType that should be passed when +// constructing a ParsedInternalKey object for seeking to a particular +// sequence number (since we sort sequence numbers in decreasing order +// and the value type is embedded as the low 8 bits in the sequence +// number in internal keys, we need to use the highest-numbered +// ValueType, not the lowest). +static const ValueType kValueTypeForSeek = kTypeValue; + +typedef uint64_t SequenceNumber; + +// We leave eight bits empty at the bottom so a type and sequence# +// can be packed together into 64-bits. +static const SequenceNumber kMaxSequenceNumber = + ((0x1ull << 56) - 1); + +struct ParsedInternalKey { + Slice user_key; + SequenceNumber sequence; + ValueType type; + + ParsedInternalKey() { } // Intentionally left uninitialized (for speed) + ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t) + : user_key(u), sequence(seq), type(t) { } + std::string DebugString() const; +}; + +// Return the length of the encoding of "key". +inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) { + return key.user_key.size() + 8; +} + +// Append the serialization of "key" to *result. +extern void AppendInternalKey(std::string* result, + const ParsedInternalKey& key); + +// Attempt to parse an internal key from "internal_key". On success, +// stores the parsed data in "*result", and returns true. +// +// On error, returns false, leaves "*result" in an undefined state. +extern bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result); + +// Returns the user key portion of an internal key. +inline Slice ExtractUserKey(const Slice& internal_key) { + assert(internal_key.size() >= 8); + return Slice(internal_key.data(), internal_key.size() - 8); +} + +inline ValueType ExtractValueType(const Slice& internal_key) { + assert(internal_key.size() >= 8); + const size_t n = internal_key.size(); + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + return static_cast<ValueType>(c); +} + +// A comparator for internal keys that uses a specified comparator for +// the user key portion and breaks ties by decreasing sequence number. +class InternalKeyComparator : public Comparator { + private: + const Comparator* user_comparator_; + public: + explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { } + virtual const char* Name() const; + virtual int Compare(const Slice& a, const Slice& b) const; + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const; + virtual void FindShortSuccessor(std::string* key) const; + + const Comparator* user_comparator() const { return user_comparator_; } + + int Compare(const InternalKey& a, const InternalKey& b) const; +}; + +// Filter policy wrapper that converts from internal keys to user keys +class InternalFilterPolicy : public FilterPolicy { + private: + const FilterPolicy* const user_policy_; + public: + explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { } + virtual const char* Name() const; + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const; + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const; +}; + +// Modules in this directory should keep internal keys wrapped inside +// the following class instead of plain strings so that we do not +// incorrectly use string comparisons instead of an InternalKeyComparator. +class InternalKey { + private: + std::string rep_; + public: + InternalKey() { } // Leave rep_ as empty to indicate it is invalid + InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) { + AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t)); + } + + void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); } + Slice Encode() const { + assert(!rep_.empty()); + return rep_; + } + + Slice user_key() const { return ExtractUserKey(rep_); } + + void SetFrom(const ParsedInternalKey& p) { + rep_.clear(); + AppendInternalKey(&rep_, p); + } + + void Clear() { rep_.clear(); } + + std::string DebugString() const; +}; + +inline int InternalKeyComparator::Compare( + const InternalKey& a, const InternalKey& b) const { + return Compare(a.Encode(), b.Encode()); +} + +inline bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result) { + const size_t n = internal_key.size(); + if (n < 8) return false; + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + result->sequence = num >> 8; + result->type = static_cast<ValueType>(c); + result->user_key = Slice(internal_key.data(), n - 8); + return (c <= static_cast<unsigned char>(kTypeValue)); +} + +// A helper class useful for DBImpl::Get() +class LookupKey { + public: + // Initialize *this for looking up user_key at a snapshot with + // the specified sequence number. + LookupKey(const Slice& user_key, SequenceNumber sequence); + + ~LookupKey(); + + // Return a key suitable for lookup in a MemTable. + Slice memtable_key() const { return Slice(start_, end_ - start_); } + + // Return an internal key (suitable for passing to an internal iterator) + Slice internal_key() const { return Slice(kstart_, end_ - kstart_); } + + // Return the user key + Slice user_key() const { return Slice(kstart_, end_ - kstart_ - 8); } + + private: + // We construct a char array of the form: + // klength varint32 <-- start_ + // userkey char[klength] <-- kstart_ + // tag uint64 + // <-- end_ + // The array is a suitable MemTable key. + // The suffix starting with "userkey" can be used as an InternalKey. + const char* start_; + const char* kstart_; + const char* end_; + char space_[200]; // Avoid allocation for short keys + + // No copying allowed + LookupKey(const LookupKey&); + void operator=(const LookupKey&); +}; + +inline LookupKey::~LookupKey() { + if (start_ != space_) delete[] start_; +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FORMAT_H_ diff --git a/src/leveldb/db/dbformat_test.cc b/src/leveldb/db/dbformat_test.cc new file mode 100644 index 0000000000..5d82f5d313 --- /dev/null +++ b/src/leveldb/db/dbformat_test.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/dbformat.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string IKey(const std::string& user_key, + uint64_t seq, + ValueType vt) { + std::string encoded; + AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt)); + return encoded; +} + +static std::string Shorten(const std::string& s, const std::string& l) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortestSeparator(&result, l); + return result; +} + +static std::string ShortSuccessor(const std::string& s) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortSuccessor(&result); + return result; +} + +static void TestKey(const std::string& key, + uint64_t seq, + ValueType vt) { + std::string encoded = IKey(key, seq, vt); + + Slice in(encoded); + ParsedInternalKey decoded("", 0, kTypeValue); + + ASSERT_TRUE(ParseInternalKey(in, &decoded)); + ASSERT_EQ(key, decoded.user_key.ToString()); + ASSERT_EQ(seq, decoded.sequence); + ASSERT_EQ(vt, decoded.type); + + ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded)); +} + +class FormatTest { }; + +TEST(FormatTest, InternalKey_EncodeDecode) { + const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" }; + const uint64_t seq[] = { + 1, 2, 3, + (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1, + (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1, + (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1 + }; + for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) { + for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) { + TestKey(keys[k], seq[s], kTypeValue); + TestKey("hello", 1, kTypeDeletion); + } + } +} + +TEST(FormatTest, InternalKeyShortSeparator) { + // When user keys are same + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 99, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 101, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeDeletion))); + + // When user keys are misordered + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("bar", 99, kTypeValue))); + + // When user keys are different, but correctly ordered + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + Shorten(IKey("foo", 100, kTypeValue), + IKey("hello", 200, kTypeValue))); + + // When start user key is prefix of limit user key + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foobar", 200, kTypeValue))); + + // When limit user key is prefix of start user key + ASSERT_EQ(IKey("foobar", 100, kTypeValue), + Shorten(IKey("foobar", 100, kTypeValue), + IKey("foo", 200, kTypeValue))); +} + +TEST(FormatTest, InternalKeyShortestSuccessor) { + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + ShortSuccessor(IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue), + ShortSuccessor(IKey("\xff\xff", 100, kTypeValue))); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/filename.cc b/src/leveldb/db/filename.cc new file mode 100644 index 0000000000..3c4d49f64e --- /dev/null +++ b/src/leveldb/db/filename.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <ctype.h> +#include <stdio.h> +#include "db/filename.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "util/logging.h" + +namespace leveldb { + +// A utility routine: write "data" to the named file and Sync() it. +extern Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname); + +static std::string MakeFileName(const std::string& name, uint64_t number, + const char* suffix) { + char buf[100]; + snprintf(buf, sizeof(buf), "/%06llu.%s", + static_cast<unsigned long long>(number), + suffix); + return name + buf; +} + +std::string LogFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "log"); +} + +std::string TableFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "sst"); +} + +std::string DescriptorFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + char buf[100]; + snprintf(buf, sizeof(buf), "/MANIFEST-%06llu", + static_cast<unsigned long long>(number)); + return dbname + buf; +} + +std::string CurrentFileName(const std::string& dbname) { + return dbname + "/CURRENT"; +} + +std::string LockFileName(const std::string& dbname) { + return dbname + "/LOCK"; +} + +std::string TempFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + return MakeFileName(dbname, number, "dbtmp"); +} + +std::string InfoLogFileName(const std::string& dbname) { + return dbname + "/LOG"; +} + +// Return the name of the old info log file for "dbname". +std::string OldInfoLogFileName(const std::string& dbname) { + return dbname + "/LOG.old"; +} + + +// Owned filenames have the form: +// dbname/CURRENT +// dbname/LOCK +// dbname/LOG +// dbname/LOG.old +// dbname/MANIFEST-[0-9]+ +// dbname/[0-9]+.(log|sst) +bool ParseFileName(const std::string& fname, + uint64_t* number, + FileType* type) { + Slice rest(fname); + if (rest == "CURRENT") { + *number = 0; + *type = kCurrentFile; + } else if (rest == "LOCK") { + *number = 0; + *type = kDBLockFile; + } else if (rest == "LOG" || rest == "LOG.old") { + *number = 0; + *type = kInfoLogFile; + } else if (rest.starts_with("MANIFEST-")) { + rest.remove_prefix(strlen("MANIFEST-")); + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + if (!rest.empty()) { + return false; + } + *type = kDescriptorFile; + *number = num; + } else { + // Avoid strtoull() to keep filename format independent of the + // current locale + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + Slice suffix = rest; + if (suffix == Slice(".log")) { + *type = kLogFile; + } else if (suffix == Slice(".sst")) { + *type = kTableFile; + } else if (suffix == Slice(".dbtmp")) { + *type = kTempFile; + } else { + return false; + } + *number = num; + } + return true; +} + +Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number) { + // Remove leading "dbname/" and add newline to manifest file name + std::string manifest = DescriptorFileName(dbname, descriptor_number); + Slice contents = manifest; + assert(contents.starts_with(dbname + "/")); + contents.remove_prefix(dbname.size() + 1); + std::string tmp = TempFileName(dbname, descriptor_number); + Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp); + if (s.ok()) { + s = env->RenameFile(tmp, CurrentFileName(dbname)); + } + if (!s.ok()) { + env->DeleteFile(tmp); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb/db/filename.h b/src/leveldb/db/filename.h new file mode 100644 index 0000000000..d5d09b1146 --- /dev/null +++ b/src/leveldb/db/filename.h @@ -0,0 +1,80 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// File names used by DB code + +#ifndef STORAGE_LEVELDB_DB_FILENAME_H_ +#define STORAGE_LEVELDB_DB_FILENAME_H_ + +#include <stdint.h> +#include <string> +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +enum FileType { + kLogFile, + kDBLockFile, + kTableFile, + kDescriptorFile, + kCurrentFile, + kTempFile, + kInfoLogFile // Either the current one, or an old one +}; + +// Return the name of the log file with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string LogFileName(const std::string& dbname, uint64_t number); + +// Return the name of the sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string TableFileName(const std::string& dbname, uint64_t number); + +// Return the name of the descriptor file for the db named by +// "dbname" and the specified incarnation number. The result will be +// prefixed with "dbname". +extern std::string DescriptorFileName(const std::string& dbname, + uint64_t number); + +// Return the name of the current file. This file contains the name +// of the current manifest file. The result will be prefixed with +// "dbname". +extern std::string CurrentFileName(const std::string& dbname); + +// Return the name of the lock file for the db named by +// "dbname". The result will be prefixed with "dbname". +extern std::string LockFileName(const std::string& dbname); + +// Return the name of a temporary file owned by the db named "dbname". +// The result will be prefixed with "dbname". +extern std::string TempFileName(const std::string& dbname, uint64_t number); + +// Return the name of the info log file for "dbname". +extern std::string InfoLogFileName(const std::string& dbname); + +// Return the name of the old info log file for "dbname". +extern std::string OldInfoLogFileName(const std::string& dbname); + +// If filename is a leveldb file, store the type of the file in *type. +// The number encoded in the filename is stored in *number. If the +// filename was successfully parsed, returns true. Else return false. +extern bool ParseFileName(const std::string& filename, + uint64_t* number, + FileType* type); + +// Make the CURRENT file point to the descriptor file with the +// specified number. +extern Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number); + + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FILENAME_H_ diff --git a/src/leveldb/db/filename_test.cc b/src/leveldb/db/filename_test.cc new file mode 100644 index 0000000000..47353d6c9a --- /dev/null +++ b/src/leveldb/db/filename_test.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/filename.h" + +#include "db/dbformat.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +class FileNameTest { }; + +TEST(FileNameTest, Parse) { + Slice db; + FileType type; + uint64_t number; + + // Successful parses + static struct { + const char* fname; + uint64_t number; + FileType type; + } cases[] = { + { "100.log", 100, kLogFile }, + { "0.log", 0, kLogFile }, + { "0.sst", 0, kTableFile }, + { "CURRENT", 0, kCurrentFile }, + { "LOCK", 0, kDBLockFile }, + { "MANIFEST-2", 2, kDescriptorFile }, + { "MANIFEST-7", 7, kDescriptorFile }, + { "LOG", 0, kInfoLogFile }, + { "LOG.old", 0, kInfoLogFile }, + { "18446744073709551615.log", 18446744073709551615ull, kLogFile }, + }; + for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + std::string f = cases[i].fname; + ASSERT_TRUE(ParseFileName(f, &number, &type)) << f; + ASSERT_EQ(cases[i].type, type) << f; + ASSERT_EQ(cases[i].number, number) << f; + } + + // Errors + static const char* errors[] = { + "", + "foo", + "foo-dx-100.log", + ".log", + "", + "manifest", + "CURREN", + "CURRENTX", + "MANIFES", + "MANIFEST", + "MANIFEST-", + "XMANIFEST-3", + "MANIFEST-3x", + "LOC", + "LOCKx", + "LO", + "LOGx", + "18446744073709551616.log", + "184467440737095516150.log", + "100", + "100.", + "100.lop" + }; + for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { + std::string f = errors[i]; + ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; + }; +} + +TEST(FileNameTest, Construction) { + uint64_t number; + FileType type; + std::string fname; + + fname = CurrentFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kCurrentFile, type); + + fname = LockFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kDBLockFile, type); + + fname = LogFileName("foo", 192); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(192, number); + ASSERT_EQ(kLogFile, type); + + fname = TableFileName("bar", 200); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(200, number); + ASSERT_EQ(kTableFile, type); + + fname = DescriptorFileName("bar", 100); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(100, number); + ASSERT_EQ(kDescriptorFile, type); + + fname = TempFileName("tmp", 999); + ASSERT_EQ("tmp/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(999, number); + ASSERT_EQ(kTempFile, type); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/log_format.h b/src/leveldb/db/log_format.h new file mode 100644 index 0000000000..2690cb9789 --- /dev/null +++ b/src/leveldb/db/log_format.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Log format information shared by reader and writer. +// See ../doc/log_format.txt for more detail. + +#ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_ +#define STORAGE_LEVELDB_DB_LOG_FORMAT_H_ + +namespace leveldb { +namespace log { + +enum RecordType { + // Zero is reserved for preallocated files + kZeroType = 0, + + kFullType = 1, + + // For fragments + kFirstType = 2, + kMiddleType = 3, + kLastType = 4 +}; +static const int kMaxRecordType = kLastType; + +static const int kBlockSize = 32768; + +// Header is checksum (4 bytes), type (1 byte), length (2 bytes). +static const int kHeaderSize = 4 + 1 + 2; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_ diff --git a/src/leveldb/db/log_reader.cc b/src/leveldb/db/log_reader.cc new file mode 100644 index 0000000000..b35f115aad --- /dev/null +++ b/src/leveldb/db/log_reader.cc @@ -0,0 +1,259 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" + +#include <stdio.h> +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Reader::Reporter::~Reporter() { +} + +Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset) + : file_(file), + reporter_(reporter), + checksum_(checksum), + backing_store_(new char[kBlockSize]), + buffer_(), + eof_(false), + last_record_offset_(0), + end_of_buffer_offset_(0), + initial_offset_(initial_offset) { +} + +Reader::~Reader() { + delete[] backing_store_; +} + +bool Reader::SkipToInitialBlock() { + size_t offset_in_block = initial_offset_ % kBlockSize; + uint64_t block_start_location = initial_offset_ - offset_in_block; + + // Don't search a block if we'd be in the trailer + if (offset_in_block > kBlockSize - 6) { + offset_in_block = 0; + block_start_location += kBlockSize; + } + + end_of_buffer_offset_ = block_start_location; + + // Skip to start of first block that can contain the initial record + if (block_start_location > 0) { + Status skip_status = file_->Skip(block_start_location); + if (!skip_status.ok()) { + ReportDrop(block_start_location, skip_status); + return false; + } + } + + return true; +} + +bool Reader::ReadRecord(Slice* record, std::string* scratch) { + if (last_record_offset_ < initial_offset_) { + if (!SkipToInitialBlock()) { + return false; + } + } + + scratch->clear(); + record->clear(); + bool in_fragmented_record = false; + // Record offset of the logical record that we're reading + // 0 is a dummy value to make compilers happy + uint64_t prospective_record_offset = 0; + + Slice fragment; + while (true) { + uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size(); + const unsigned int record_type = ReadPhysicalRecord(&fragment); + switch (record_type) { + case kFullType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(1)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->clear(); + *record = fragment; + last_record_offset_ = prospective_record_offset; + return true; + + case kFirstType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(2)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->assign(fragment.data(), fragment.size()); + in_fragmented_record = true; + break; + + case kMiddleType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(1)"); + } else { + scratch->append(fragment.data(), fragment.size()); + } + break; + + case kLastType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(2)"); + } else { + scratch->append(fragment.data(), fragment.size()); + *record = Slice(*scratch); + last_record_offset_ = prospective_record_offset; + return true; + } + break; + + case kEof: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "partial record without end(3)"); + scratch->clear(); + } + return false; + + case kBadRecord: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "error in middle of record"); + in_fragmented_record = false; + scratch->clear(); + } + break; + + default: { + char buf[40]; + snprintf(buf, sizeof(buf), "unknown record type %u", record_type); + ReportCorruption( + (fragment.size() + (in_fragmented_record ? scratch->size() : 0)), + buf); + in_fragmented_record = false; + scratch->clear(); + break; + } + } + } + return false; +} + +uint64_t Reader::LastRecordOffset() { + return last_record_offset_; +} + +void Reader::ReportCorruption(size_t bytes, const char* reason) { + ReportDrop(bytes, Status::Corruption(reason)); +} + +void Reader::ReportDrop(size_t bytes, const Status& reason) { + if (reporter_ != NULL && + end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) { + reporter_->Corruption(bytes, reason); + } +} + +unsigned int Reader::ReadPhysicalRecord(Slice* result) { + while (true) { + if (buffer_.size() < kHeaderSize) { + if (!eof_) { + // Last read was a full read, so this is a trailer to skip + buffer_.clear(); + Status status = file_->Read(kBlockSize, &buffer_, backing_store_); + end_of_buffer_offset_ += buffer_.size(); + if (!status.ok()) { + buffer_.clear(); + ReportDrop(kBlockSize, status); + eof_ = true; + return kEof; + } else if (buffer_.size() < kBlockSize) { + eof_ = true; + } + continue; + } else if (buffer_.size() == 0) { + // End of file + return kEof; + } else { + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "truncated record at end of file"); + return kEof; + } + } + + // Parse the header + const char* header = buffer_.data(); + const uint32_t a = static_cast<uint32_t>(header[4]) & 0xff; + const uint32_t b = static_cast<uint32_t>(header[5]) & 0xff; + const unsigned int type = header[6]; + const uint32_t length = a | (b << 8); + if (kHeaderSize + length > buffer_.size()) { + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "bad record length"); + return kBadRecord; + } + + if (type == kZeroType && length == 0) { + // Skip zero length record without reporting any drops since + // such records are produced by the mmap based writing code in + // env_posix.cc that preallocates file regions. + buffer_.clear(); + return kBadRecord; + } + + // Check crc + if (checksum_) { + uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header)); + uint32_t actual_crc = crc32c::Value(header + 6, 1 + length); + if (actual_crc != expected_crc) { + // Drop the rest of the buffer since "length" itself may have + // been corrupted and if we trust it, we could find some + // fragment of a real log record that just happens to look + // like a valid log record. + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "checksum mismatch"); + return kBadRecord; + } + } + + buffer_.remove_prefix(kHeaderSize + length); + + // Skip physical record that started before initial_offset_ + if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length < + initial_offset_) { + result->clear(); + return kBadRecord; + } + + *result = Slice(header + kHeaderSize, length); + return type; + } +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb/db/log_reader.h b/src/leveldb/db/log_reader.h new file mode 100644 index 0000000000..82d4bee68d --- /dev/null +++ b/src/leveldb/db/log_reader.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_READER_H_ +#define STORAGE_LEVELDB_DB_LOG_READER_H_ + +#include <stdint.h> + +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class SequentialFile; + +namespace log { + +class Reader { + public: + // Interface for reporting errors. + class Reporter { + public: + virtual ~Reporter(); + + // Some corruption was detected. "size" is the approximate number + // of bytes dropped due to the corruption. + virtual void Corruption(size_t bytes, const Status& status) = 0; + }; + + // Create a reader that will return log records from "*file". + // "*file" must remain live while this Reader is in use. + // + // If "reporter" is non-NULL, it is notified whenever some data is + // dropped due to a detected corruption. "*reporter" must remain + // live while this Reader is in use. + // + // If "checksum" is true, verify checksums if available. + // + // The Reader will start reading at the first record located at physical + // position >= initial_offset within the file. + Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset); + + ~Reader(); + + // Read the next record into *record. Returns true if read + // successfully, false if we hit end of the input. May use + // "*scratch" as temporary storage. The contents filled in *record + // will only be valid until the next mutating operation on this + // reader or the next mutation to *scratch. + bool ReadRecord(Slice* record, std::string* scratch); + + // Returns the physical offset of the last record returned by ReadRecord. + // + // Undefined before the first call to ReadRecord. + uint64_t LastRecordOffset(); + + private: + SequentialFile* const file_; + Reporter* const reporter_; + bool const checksum_; + char* const backing_store_; + Slice buffer_; + bool eof_; // Last Read() indicated EOF by returning < kBlockSize + + // Offset of the last record returned by ReadRecord. + uint64_t last_record_offset_; + // Offset of the first location past the end of buffer_. + uint64_t end_of_buffer_offset_; + + // Offset at which to start looking for the first record to return + uint64_t const initial_offset_; + + // Extend record types with the following special values + enum { + kEof = kMaxRecordType + 1, + // Returned whenever we find an invalid physical record. + // Currently there are three situations in which this happens: + // * The record has an invalid CRC (ReadPhysicalRecord reports a drop) + // * The record is a 0-length record (No drop is reported) + // * The record is below constructor's initial_offset (No drop is reported) + kBadRecord = kMaxRecordType + 2 + }; + + // Skips all blocks that are completely before "initial_offset_". + // + // Returns true on success. Handles reporting. + bool SkipToInitialBlock(); + + // Return type, or one of the preceding special values + unsigned int ReadPhysicalRecord(Slice* result); + + // Reports dropped bytes to the reporter. + // buffer_ must be updated to remove the dropped bytes prior to invocation. + void ReportCorruption(size_t bytes, const char* reason); + void ReportDrop(size_t bytes, const Status& reason); + + // No copying allowed + Reader(const Reader&); + void operator=(const Reader&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_READER_H_ diff --git a/src/leveldb/db/log_test.cc b/src/leveldb/db/log_test.cc new file mode 100644 index 0000000000..4c5cf87573 --- /dev/null +++ b/src/leveldb/db/log_test.cc @@ -0,0 +1,500 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { +namespace log { + +// Construct a string of the specified length made out of the supplied +// partial string. +static std::string BigString(const std::string& partial_string, size_t n) { + std::string result; + while (result.size() < n) { + result.append(partial_string); + } + result.resize(n); + return result; +} + +// Construct a string from a number +static std::string NumberString(int n) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d.", n); + return std::string(buf); +} + +// Return a skewed potentially long string +static std::string RandomSkewedString(int i, Random* rnd) { + return BigString(NumberString(i), rnd->Skewed(17)); +} + +class LogTest { + private: + class StringDest : public WritableFile { + public: + std::string contents_; + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + virtual Status Append(const Slice& slice) { + contents_.append(slice.data(), slice.size()); + return Status::OK(); + } + }; + + class StringSource : public SequentialFile { + public: + Slice contents_; + bool force_error_; + bool returned_partial_; + StringSource() : force_error_(false), returned_partial_(false) { } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; + + if (force_error_) { + force_error_ = false; + returned_partial_ = true; + return Status::Corruption("read error"); + } + + if (contents_.size() < n) { + n = contents_.size(); + returned_partial_ = true; + } + *result = Slice(contents_.data(), n); + contents_.remove_prefix(n); + return Status::OK(); + } + + virtual Status Skip(uint64_t n) { + if (n > contents_.size()) { + contents_.clear(); + return Status::NotFound("in-memory file skipepd past end"); + } + + contents_.remove_prefix(n); + + return Status::OK(); + } + }; + + class ReportCollector : public Reader::Reporter { + public: + size_t dropped_bytes_; + std::string message_; + + ReportCollector() : dropped_bytes_(0) { } + virtual void Corruption(size_t bytes, const Status& status) { + dropped_bytes_ += bytes; + message_.append(status.ToString()); + } + }; + + StringDest dest_; + StringSource source_; + ReportCollector report_; + bool reading_; + Writer writer_; + Reader reader_; + + // Record metadata for testing initial offset functionality + static size_t initial_offset_record_sizes_[]; + static uint64_t initial_offset_last_record_offsets_[]; + + public: + LogTest() : reading_(false), + writer_(&dest_), + reader_(&source_, &report_, true/*checksum*/, + 0/*initial_offset*/) { + } + + void Write(const std::string& msg) { + ASSERT_TRUE(!reading_) << "Write() after starting to read"; + writer_.AddRecord(Slice(msg)); + } + + size_t WrittenBytes() const { + return dest_.contents_.size(); + } + + std::string Read() { + if (!reading_) { + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + } + std::string scratch; + Slice record; + if (reader_.ReadRecord(&record, &scratch)) { + return record.ToString(); + } else { + return "EOF"; + } + } + + void IncrementByte(int offset, int delta) { + dest_.contents_[offset] += delta; + } + + void SetByte(int offset, char new_byte) { + dest_.contents_[offset] = new_byte; + } + + void ShrinkSize(int bytes) { + dest_.contents_.resize(dest_.contents_.size() - bytes); + } + + void FixChecksum(int header_offset, int len) { + // Compute crc of type/len/data + uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len); + crc = crc32c::Mask(crc); + EncodeFixed32(&dest_.contents_[header_offset], crc); + } + + void ForceError() { + source_.force_error_ = true; + } + + size_t DroppedBytes() const { + return report_.dropped_bytes_; + } + + std::string ReportMessage() const { + return report_.message_; + } + + // Returns OK iff recorded error message contains "msg" + std::string MatchError(const std::string& msg) const { + if (report_.message_.find(msg) == std::string::npos) { + return report_.message_; + } else { + return "OK"; + } + } + + void WriteInitialOffsetLog() { + for (int i = 0; i < 4; i++) { + std::string record(initial_offset_record_sizes_[i], + static_cast<char>('a' + i)); + Write(record); + } + } + + void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + WrittenBytes() + offset_past_end); + Slice record; + std::string scratch; + ASSERT_TRUE(!offset_reader->ReadRecord(&record, &scratch)); + delete offset_reader; + } + + void CheckInitialOffsetRecord(uint64_t initial_offset, + int expected_record_offset) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + initial_offset); + Slice record; + std::string scratch; + ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); + ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], + record.size()); + ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], + offset_reader->LastRecordOffset()); + ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + delete offset_reader; + } + +}; + +size_t LogTest::initial_offset_record_sizes_[] = + {10000, // Two sizable records in first block + 10000, + 2 * log::kBlockSize - 1000, // Span three blocks + 1}; + +uint64_t LogTest::initial_offset_last_record_offsets_[] = + {0, + kHeaderSize + 10000, + 2 * (kHeaderSize + 10000), + 2 * (kHeaderSize + 10000) + + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize}; + + +TEST(LogTest, Empty) { + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, ReadWrite) { + Write("foo"); + Write("bar"); + Write(""); + Write("xxxx"); + ASSERT_EQ("foo", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("xxxx", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("EOF", Read()); // Make sure reads at eof work +} + +TEST(LogTest, ManyBlocks) { + for (int i = 0; i < 100000; i++) { + Write(NumberString(i)); + } + for (int i = 0; i < 100000; i++) { + ASSERT_EQ(NumberString(i), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, Fragmentation) { + Write("small"); + Write(BigString("medium", 50000)); + Write(BigString("large", 100000)); + ASSERT_EQ("small", Read()); + ASSERT_EQ(BigString("medium", 50000), Read()); + ASSERT_EQ(BigString("large", 100000), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer2) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, ShortTrailer) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, AlignedEof) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, RandomRead) { + const int N = 500; + Random write_rnd(301); + for (int i = 0; i < N; i++) { + Write(RandomSkewedString(i, &write_rnd)); + } + Random read_rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_EQ(RandomSkewedString(i, &read_rnd), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +// Tests of all the error paths in log_reader.cc follow: + +TEST(LogTest, ReadError) { + Write("foo"); + ForceError(); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kBlockSize, DroppedBytes()); + ASSERT_EQ("OK", MatchError("read error")); +} + +TEST(LogTest, BadRecordType) { + Write("foo"); + // Type is stored in header[6] + IncrementByte(6, 100); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("unknown record type")); +} + +TEST(LogTest, TruncatedTrailingRecord) { + Write("foo"); + ShrinkSize(4); // Drop all payload as well as a header byte + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kHeaderSize - 1, DroppedBytes()); + ASSERT_EQ("OK", MatchError("truncated record at end of file")); +} + +TEST(LogTest, BadLength) { + Write("foo"); + ShrinkSize(1); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kHeaderSize + 2, DroppedBytes()); + ASSERT_EQ("OK", MatchError("bad record length")); +} + +TEST(LogTest, ChecksumMismatch) { + Write("foo"); + IncrementByte(0, 10); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(10, DroppedBytes()); + ASSERT_EQ("OK", MatchError("checksum mismatch")); +} + +TEST(LogTest, UnexpectedMiddleType) { + Write("foo"); + SetByte(6, kMiddleType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedLastType) { + Write("foo"); + SetByte(6, kLastType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedFullType) { + Write("foo"); + Write("bar"); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, UnexpectedFirstType) { + Write("foo"); + Write(BigString("bar", 100000)); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ(BigString("bar", 100000), Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, ErrorJoinsRecords) { + // Consider two fragmented records: + // first(R1) last(R1) first(R2) last(R2) + // where the middle two fragments disappear. We do not want + // first(R1),last(R2) to get joined and returned as a valid record. + + // Write records that span two blocks + Write(BigString("foo", kBlockSize)); + Write(BigString("bar", kBlockSize)); + Write("correct"); + + // Wipe the middle block + for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) { + SetByte(offset, 'x'); + } + + ASSERT_EQ("correct", Read()); + ASSERT_EQ("EOF", Read()); + const int dropped = DroppedBytes(); + ASSERT_LE(dropped, 2*kBlockSize + 100); + ASSERT_GE(dropped, 2*kBlockSize); +} + +TEST(LogTest, ReadStart) { + CheckInitialOffsetRecord(0, 0); +} + +TEST(LogTest, ReadSecondOneOff) { + CheckInitialOffsetRecord(1, 1); +} + +TEST(LogTest, ReadSecondTenThousand) { + CheckInitialOffsetRecord(10000, 1); +} + +TEST(LogTest, ReadSecondStart) { + CheckInitialOffsetRecord(10007, 1); +} + +TEST(LogTest, ReadThirdOneOff) { + CheckInitialOffsetRecord(10008, 2); +} + +TEST(LogTest, ReadThirdStart) { + CheckInitialOffsetRecord(20014, 2); +} + +TEST(LogTest, ReadFourthOneOff) { + CheckInitialOffsetRecord(20015, 3); +} + +TEST(LogTest, ReadFourthFirstBlockTrailer) { + CheckInitialOffsetRecord(log::kBlockSize - 4, 3); +} + +TEST(LogTest, ReadFourthMiddleBlock) { + CheckInitialOffsetRecord(log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthLastBlock) { + CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthStart) { + CheckInitialOffsetRecord( + 2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 3); +} + +TEST(LogTest, ReadEnd) { + CheckOffsetPastEndReturnsNoRecords(0); +} + +TEST(LogTest, ReadPastEnd) { + CheckOffsetPastEndReturnsNoRecords(5); +} + +} // namespace log +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/log_writer.cc b/src/leveldb/db/log_writer.cc new file mode 100644 index 0000000000..2da99ac088 --- /dev/null +++ b/src/leveldb/db/log_writer.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_writer.h" + +#include <stdint.h> +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Writer::Writer(WritableFile* dest) + : dest_(dest), + block_offset_(0) { + for (int i = 0; i <= kMaxRecordType; i++) { + char t = static_cast<char>(i); + type_crc_[i] = crc32c::Value(&t, 1); + } +} + +Writer::~Writer() { +} + +Status Writer::AddRecord(const Slice& slice) { + const char* ptr = slice.data(); + size_t left = slice.size(); + + // Fragment the record if necessary and emit it. Note that if slice + // is empty, we still want to iterate once to emit a single + // zero-length record + Status s; + bool begin = true; + do { + const int leftover = kBlockSize - block_offset_; + assert(leftover >= 0); + if (leftover < kHeaderSize) { + // Switch to a new block + if (leftover > 0) { + // Fill the trailer (literal below relies on kHeaderSize being 7) + assert(kHeaderSize == 7); + dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); + } + block_offset_ = 0; + } + + // Invariant: we never leave < kHeaderSize bytes in a block. + assert(kBlockSize - block_offset_ - kHeaderSize >= 0); + + const size_t avail = kBlockSize - block_offset_ - kHeaderSize; + const size_t fragment_length = (left < avail) ? left : avail; + + RecordType type; + const bool end = (left == fragment_length); + if (begin && end) { + type = kFullType; + } else if (begin) { + type = kFirstType; + } else if (end) { + type = kLastType; + } else { + type = kMiddleType; + } + + s = EmitPhysicalRecord(type, ptr, fragment_length); + ptr += fragment_length; + left -= fragment_length; + begin = false; + } while (s.ok() && left > 0); + return s; +} + +Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) { + assert(n <= 0xffff); // Must fit in two bytes + assert(block_offset_ + kHeaderSize + n <= kBlockSize); + + // Format the header + char buf[kHeaderSize]; + buf[4] = static_cast<char>(n & 0xff); + buf[5] = static_cast<char>(n >> 8); + buf[6] = static_cast<char>(t); + + // Compute the crc of the record type and the payload. + uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n); + crc = crc32c::Mask(crc); // Adjust for storage + EncodeFixed32(buf, crc); + + // Write the header and the payload + Status s = dest_->Append(Slice(buf, kHeaderSize)); + if (s.ok()) { + s = dest_->Append(Slice(ptr, n)); + if (s.ok()) { + s = dest_->Flush(); + } + } + block_offset_ += kHeaderSize + n; + return s; +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb/db/log_writer.h b/src/leveldb/db/log_writer.h new file mode 100644 index 0000000000..a3a954d967 --- /dev/null +++ b/src/leveldb/db/log_writer.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_WRITER_H_ +#define STORAGE_LEVELDB_DB_LOG_WRITER_H_ + +#include <stdint.h> +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class WritableFile; + +namespace log { + +class Writer { + public: + // Create a writer that will append data to "*dest". + // "*dest" must be initially empty. + // "*dest" must remain live while this Writer is in use. + explicit Writer(WritableFile* dest); + ~Writer(); + + Status AddRecord(const Slice& slice); + + private: + WritableFile* dest_; + int block_offset_; // Current offset in block + + // crc32c values for all supported record types. These are + // pre-computed to reduce the overhead of computing the crc of the + // record type stored in the header. + uint32_t type_crc_[kMaxRecordType + 1]; + + Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); + + // No copying allowed + Writer(const Writer&); + void operator=(const Writer&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_WRITER_H_ diff --git a/src/leveldb/db/memtable.cc b/src/leveldb/db/memtable.cc new file mode 100644 index 0000000000..bfec0a7e7a --- /dev/null +++ b/src/leveldb/db/memtable.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/memtable.h" +#include "db/dbformat.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "util/coding.h" + +namespace leveldb { + +static Slice GetLengthPrefixedSlice(const char* data) { + uint32_t len; + const char* p = data; + p = GetVarint32Ptr(p, p + 5, &len); // +5: we assume "p" is not corrupted + return Slice(p, len); +} + +MemTable::MemTable(const InternalKeyComparator& cmp) + : comparator_(cmp), + refs_(0), + table_(comparator_, &arena_) { +} + +MemTable::~MemTable() { + assert(refs_ == 0); +} + +size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); } + +int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr) + const { + // Internal keys are encoded as length-prefixed strings. + Slice a = GetLengthPrefixedSlice(aptr); + Slice b = GetLengthPrefixedSlice(bptr); + return comparator.Compare(a, b); +} + +// Encode a suitable internal key target for "target" and return it. +// Uses *scratch as scratch space, and the returned pointer will point +// into this scratch space. +static const char* EncodeKey(std::string* scratch, const Slice& target) { + scratch->clear(); + PutVarint32(scratch, target.size()); + scratch->append(target.data(), target.size()); + return scratch->data(); +} + +class MemTableIterator: public Iterator { + public: + explicit MemTableIterator(MemTable::Table* table) : iter_(table) { } + + virtual bool Valid() const { return iter_.Valid(); } + virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); } + virtual void SeekToFirst() { iter_.SeekToFirst(); } + virtual void SeekToLast() { iter_.SeekToLast(); } + virtual void Next() { iter_.Next(); } + virtual void Prev() { iter_.Prev(); } + virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); } + virtual Slice value() const { + Slice key_slice = GetLengthPrefixedSlice(iter_.key()); + return GetLengthPrefixedSlice(key_slice.data() + key_slice.size()); + } + + virtual Status status() const { return Status::OK(); } + + private: + MemTable::Table::Iterator iter_; + std::string tmp_; // For passing to EncodeKey + + // No copying allowed + MemTableIterator(const MemTableIterator&); + void operator=(const MemTableIterator&); +}; + +Iterator* MemTable::NewIterator() { + return new MemTableIterator(&table_); +} + +void MemTable::Add(SequenceNumber s, ValueType type, + const Slice& key, + const Slice& value) { + // Format of an entry is concatenation of: + // key_size : varint32 of internal_key.size() + // key bytes : char[internal_key.size()] + // value_size : varint32 of value.size() + // value bytes : char[value.size()] + size_t key_size = key.size(); + size_t val_size = value.size(); + size_t internal_key_size = key_size + 8; + const size_t encoded_len = + VarintLength(internal_key_size) + internal_key_size + + VarintLength(val_size) + val_size; + char* buf = arena_.Allocate(encoded_len); + char* p = EncodeVarint32(buf, internal_key_size); + memcpy(p, key.data(), key_size); + p += key_size; + EncodeFixed64(p, (s << 8) | type); + p += 8; + p = EncodeVarint32(p, val_size); + memcpy(p, value.data(), val_size); + assert((p + val_size) - buf == encoded_len); + table_.Insert(buf); +} + +bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) { + Slice memkey = key.memtable_key(); + Table::Iterator iter(&table_); + iter.Seek(memkey.data()); + if (iter.Valid()) { + // entry format is: + // klength varint32 + // userkey char[klength] + // tag uint64 + // vlength varint32 + // value char[vlength] + // Check that it belongs to same user key. We do not check the + // sequence number since the Seek() call above should have skipped + // all entries with overly large sequence numbers. + const char* entry = iter.key(); + uint32_t key_length; + const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length); + if (comparator_.comparator.user_comparator()->Compare( + Slice(key_ptr, key_length - 8), + key.user_key()) == 0) { + // Correct user key + const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8); + switch (static_cast<ValueType>(tag & 0xff)) { + case kTypeValue: { + Slice v = GetLengthPrefixedSlice(key_ptr + key_length); + value->assign(v.data(), v.size()); + return true; + } + case kTypeDeletion: + *s = Status::NotFound(Slice()); + return true; + } + } + } + return false; +} + +} // namespace leveldb diff --git a/src/leveldb/db/memtable.h b/src/leveldb/db/memtable.h new file mode 100644 index 0000000000..92e90bb099 --- /dev/null +++ b/src/leveldb/db/memtable.h @@ -0,0 +1,91 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_MEMTABLE_H_ +#define STORAGE_LEVELDB_DB_MEMTABLE_H_ + +#include <string> +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/skiplist.h" +#include "util/arena.h" + +namespace leveldb { + +class InternalKeyComparator; +class Mutex; +class MemTableIterator; + +class MemTable { + public: + // MemTables are reference counted. The initial reference count + // is zero and the caller must call Ref() at least once. + explicit MemTable(const InternalKeyComparator& comparator); + + // Increase reference count. + void Ref() { ++refs_; } + + // Drop reference count. Delete if no more references exist. + void Unref() { + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + delete this; + } + } + + // Returns an estimate of the number of bytes of data in use by this + // data structure. + // + // REQUIRES: external synchronization to prevent simultaneous + // operations on the same MemTable. + size_t ApproximateMemoryUsage(); + + // Return an iterator that yields the contents of the memtable. + // + // The caller must ensure that the underlying MemTable remains live + // while the returned iterator is live. The keys returned by this + // iterator are internal keys encoded by AppendInternalKey in the + // db/format.{h,cc} module. + Iterator* NewIterator(); + + // Add an entry into memtable that maps key to value at the + // specified sequence number and with the specified type. + // Typically value will be empty if type==kTypeDeletion. + void Add(SequenceNumber seq, ValueType type, + const Slice& key, + const Slice& value); + + // If memtable contains a value for key, store it in *value and return true. + // If memtable contains a deletion for key, store a NotFound() error + // in *status and return true. + // Else, return false. + bool Get(const LookupKey& key, std::string* value, Status* s); + + private: + ~MemTable(); // Private since only Unref() should be used to delete it + + struct KeyComparator { + const InternalKeyComparator comparator; + explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { } + int operator()(const char* a, const char* b) const; + }; + friend class MemTableIterator; + friend class MemTableBackwardIterator; + + typedef SkipList<const char*, KeyComparator> Table; + + KeyComparator comparator_; + int refs_; + Arena arena_; + Table table_; + + // No copying allowed + MemTable(const MemTable&); + void operator=(const MemTable&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_MEMTABLE_H_ diff --git a/src/leveldb/db/repair.cc b/src/leveldb/db/repair.cc new file mode 100644 index 0000000000..022d52f3de --- /dev/null +++ b/src/leveldb/db/repair.cc @@ -0,0 +1,389 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// We recover the contents of the descriptor from the other files we find. +// (1) Any log files are first converted to tables +// (2) We scan every table to compute +// (a) smallest/largest for the table +// (b) largest sequence number in the table +// (3) We generate descriptor contents: +// - log number is set to zero +// - next-file-number is set to 1 + largest file number we found +// - last-sequence-number is set to largest sequence# found across +// all tables (see 2c) +// - compaction pointers are cleared +// - every table file is added at level 0 +// +// Possible optimization 1: +// (a) Compute total size and use to pick appropriate max-level M +// (b) Sort tables by largest sequence# in the table +// (c) For each table: if it overlaps earlier table, place in level-0, +// else place in level-M. +// Possible optimization 2: +// Store per-table metadata (smallest, largest, largest-seq#, ...) +// in the table's meta section to speed up ScanTable. + +#include "db/builder.h" +#include "db/db_impl.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" + +namespace leveldb { + +namespace { + +class Repairer { + public: + Repairer(const std::string& dbname, const Options& options) + : dbname_(dbname), + env_(options.env), + icmp_(options.comparator), + ipolicy_(options.filter_policy), + options_(SanitizeOptions(dbname, &icmp_, &ipolicy_, options)), + owns_info_log_(options_.info_log != options.info_log), + owns_cache_(options_.block_cache != options.block_cache), + next_file_number_(1) { + // TableCache can be small since we expect each table to be opened once. + table_cache_ = new TableCache(dbname_, &options_, 10); + } + + ~Repairer() { + delete table_cache_; + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } + } + + Status Run() { + Status status = FindFiles(); + if (status.ok()) { + ConvertLogFilesToTables(); + ExtractMetaData(); + status = WriteDescriptor(); + } + if (status.ok()) { + unsigned long long bytes = 0; + for (size_t i = 0; i < tables_.size(); i++) { + bytes += tables_[i].meta.file_size; + } + Log(options_.info_log, + "**** Repaired leveldb %s; " + "recovered %d files; %llu bytes. " + "Some data may have been lost. " + "****", + dbname_.c_str(), + static_cast<int>(tables_.size()), + bytes); + } + return status; + } + + private: + struct TableInfo { + FileMetaData meta; + SequenceNumber max_sequence; + }; + + std::string const dbname_; + Env* const env_; + InternalKeyComparator const icmp_; + InternalFilterPolicy const ipolicy_; + Options const options_; + bool owns_info_log_; + bool owns_cache_; + TableCache* table_cache_; + VersionEdit edit_; + + std::vector<std::string> manifests_; + std::vector<uint64_t> table_numbers_; + std::vector<uint64_t> logs_; + std::vector<TableInfo> tables_; + uint64_t next_file_number_; + + Status FindFiles() { + std::vector<std::string> filenames; + Status status = env_->GetChildren(dbname_, &filenames); + if (!status.ok()) { + return status; + } + if (filenames.empty()) { + return Status::IOError(dbname_, "repair found no files"); + } + + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + if (type == kDescriptorFile) { + manifests_.push_back(filenames[i]); + } else { + if (number + 1 > next_file_number_) { + next_file_number_ = number + 1; + } + if (type == kLogFile) { + logs_.push_back(number); + } else if (type == kTableFile) { + table_numbers_.push_back(number); + } else { + // Ignore other files + } + } + } + } + return status; + } + + void ConvertLogFilesToTables() { + for (size_t i = 0; i < logs_.size(); i++) { + std::string logname = LogFileName(dbname_, logs_[i]); + Status status = ConvertLogToTable(logs_[i]); + if (!status.ok()) { + Log(options_.info_log, "Log #%llu: ignoring conversion error: %s", + (unsigned long long) logs_[i], + status.ToString().c_str()); + } + ArchiveFile(logname); + } + } + + Status ConvertLogToTable(uint64_t log) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + uint64_t lognum; + virtual void Corruption(size_t bytes, const Status& s) { + // We print error messages for corruption, but continue repairing. + Log(info_log, "Log #%llu: dropping %d bytes; %s", + (unsigned long long) lognum, + static_cast<int>(bytes), + s.ToString().c_str()); + } + }; + + // Open the log file + std::string logname = LogFileName(dbname_, log); + SequentialFile* lfile; + Status status = env_->NewSequentialFile(logname, &lfile); + if (!status.ok()) { + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.lognum = log; + // We intentially make log::Reader do checksumming so that + // corruptions cause entire commits to be skipped instead of + // propagating bad information (like overly large sequence + // numbers). + log::Reader reader(lfile, &reporter, false/*do not checksum*/, + 0/*initial_offset*/); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = new MemTable(icmp_); + mem->Ref(); + int counter = 0; + while (reader.ReadRecord(&record, &scratch)) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + status = WriteBatchInternal::InsertInto(&batch, mem); + if (status.ok()) { + counter += WriteBatchInternal::Count(&batch); + } else { + Log(options_.info_log, "Log #%llu: ignoring %s", + (unsigned long long) log, + status.ToString().c_str()); + status = Status::OK(); // Keep going with rest of file + } + } + delete lfile; + + // Do not record a version edit for this conversion to a Table + // since ExtractMetaData() will also generate edits. + FileMetaData meta; + meta.number = next_file_number_++; + Iterator* iter = mem->NewIterator(); + status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + delete iter; + mem->Unref(); + mem = NULL; + if (status.ok()) { + if (meta.file_size > 0) { + table_numbers_.push_back(meta.number); + } + } + Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s", + (unsigned long long) log, + counter, + (unsigned long long) meta.number, + status.ToString().c_str()); + return status; + } + + void ExtractMetaData() { + std::vector<TableInfo> kept; + for (size_t i = 0; i < table_numbers_.size(); i++) { + TableInfo t; + t.meta.number = table_numbers_[i]; + Status status = ScanTable(&t); + if (!status.ok()) { + std::string fname = TableFileName(dbname_, table_numbers_[i]); + Log(options_.info_log, "Table #%llu: ignoring %s", + (unsigned long long) table_numbers_[i], + status.ToString().c_str()); + ArchiveFile(fname); + } else { + tables_.push_back(t); + } + } + } + + Status ScanTable(TableInfo* t) { + std::string fname = TableFileName(dbname_, t->meta.number); + int counter = 0; + Status status = env_->GetFileSize(fname, &t->meta.file_size); + if (status.ok()) { + Iterator* iter = table_cache_->NewIterator( + ReadOptions(), t->meta.number, t->meta.file_size); + bool empty = true; + ParsedInternalKey parsed; + t->max_sequence = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + Slice key = iter->key(); + if (!ParseInternalKey(key, &parsed)) { + Log(options_.info_log, "Table #%llu: unparsable key %s", + (unsigned long long) t->meta.number, + EscapeString(key).c_str()); + continue; + } + + counter++; + if (empty) { + empty = false; + t->meta.smallest.DecodeFrom(key); + } + t->meta.largest.DecodeFrom(key); + if (parsed.sequence > t->max_sequence) { + t->max_sequence = parsed.sequence; + } + } + if (!iter->status().ok()) { + status = iter->status(); + } + delete iter; + } + Log(options_.info_log, "Table #%llu: %d entries %s", + (unsigned long long) t->meta.number, + counter, + status.ToString().c_str()); + return status; + } + + Status WriteDescriptor() { + std::string tmp = TempFileName(dbname_, 1); + WritableFile* file; + Status status = env_->NewWritableFile(tmp, &file); + if (!status.ok()) { + return status; + } + + SequenceNumber max_sequence = 0; + for (size_t i = 0; i < tables_.size(); i++) { + if (max_sequence < tables_[i].max_sequence) { + max_sequence = tables_[i].max_sequence; + } + } + + edit_.SetComparatorName(icmp_.user_comparator()->Name()); + edit_.SetLogNumber(0); + edit_.SetNextFile(next_file_number_); + edit_.SetLastSequence(max_sequence); + + for (size_t i = 0; i < tables_.size(); i++) { + // TODO(opt): separate out into multiple levels + const TableInfo& t = tables_[i]; + edit_.AddFile(0, t.meta.number, t.meta.file_size, + t.meta.smallest, t.meta.largest); + } + + //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); + { + log::Writer log(file); + std::string record; + edit_.EncodeTo(&record); + status = log.AddRecord(record); + } + if (status.ok()) { + status = file->Close(); + } + delete file; + file = NULL; + + if (!status.ok()) { + env_->DeleteFile(tmp); + } else { + // Discard older manifests + for (size_t i = 0; i < manifests_.size(); i++) { + ArchiveFile(dbname_ + "/" + manifests_[i]); + } + + // Install new manifest + status = env_->RenameFile(tmp, DescriptorFileName(dbname_, 1)); + if (status.ok()) { + status = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(tmp); + } + } + return status; + } + + void ArchiveFile(const std::string& fname) { + // Move into another directory. E.g., for + // dir/foo + // rename to + // dir/lost/foo + const char* slash = strrchr(fname.c_str(), '/'); + std::string new_dir; + if (slash != NULL) { + new_dir.assign(fname.data(), slash - fname.data()); + } + new_dir.append("/lost"); + env_->CreateDir(new_dir); // Ignore error + std::string new_file = new_dir; + new_file.append("/"); + new_file.append((slash == NULL) ? fname.c_str() : slash + 1); + Status s = env_->RenameFile(fname, new_file); + Log(options_.info_log, "Archiving %s: %s\n", + fname.c_str(), s.ToString().c_str()); + } +}; +} // namespace + +Status RepairDB(const std::string& dbname, const Options& options) { + Repairer repairer(dbname, options); + return repairer.Run(); +} + +} // namespace leveldb diff --git a/src/leveldb/db/skiplist.h b/src/leveldb/db/skiplist.h new file mode 100644 index 0000000000..af85be6d01 --- /dev/null +++ b/src/leveldb/db/skiplist.h @@ -0,0 +1,379 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread safety +// ------------- +// +// Writes require external synchronization, most likely a mutex. +// Reads require a guarantee that the SkipList will not be destroyed +// while the read is in progress. Apart from that, reads progress +// without any internal locking or synchronization. +// +// Invariants: +// +// (1) Allocated nodes are never deleted until the SkipList is +// destroyed. This is trivially guaranteed by the code since we +// never delete any skip list nodes. +// +// (2) The contents of a Node except for the next/prev pointers are +// immutable after the Node has been linked into the SkipList. +// Only Insert() modifies the list, and it is careful to initialize +// a node and use release-stores to publish the nodes in one or +// more lists. +// +// ... prev vs. next pointer ordering ... + +#include <assert.h> +#include <stdlib.h> +#include "port/port.h" +#include "util/arena.h" +#include "util/random.h" + +namespace leveldb { + +class Arena; + +template<typename Key, class Comparator> +class SkipList { + private: + struct Node; + + public: + // Create a new SkipList object that will use "cmp" for comparing keys, + // and will allocate memory using "*arena". Objects allocated in the arena + // must remain allocated for the lifetime of the skiplist object. + explicit SkipList(Comparator cmp, Arena* arena); + + // Insert key into the list. + // REQUIRES: nothing that compares equal to key is currently in the list. + void Insert(const Key& key); + + // Returns true iff an entry that compares equal to key is in the list. + bool Contains(const Key& key) const; + + // Iteration over the contents of a skip list + class Iterator { + public: + // Initialize an iterator over the specified list. + // The returned iterator is not valid. + explicit Iterator(const SkipList* list); + + // Returns true iff the iterator is positioned at a valid node. + bool Valid() const; + + // Returns the key at the current position. + // REQUIRES: Valid() + const Key& key() const; + + // Advances to the next position. + // REQUIRES: Valid() + void Next(); + + // Advances to the previous position. + // REQUIRES: Valid() + void Prev(); + + // Advance to the first entry with a key >= target + void Seek(const Key& target); + + // Position at the first entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToFirst(); + + // Position at the last entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToLast(); + + private: + const SkipList* list_; + Node* node_; + // Intentionally copyable + }; + + private: + enum { kMaxHeight = 12 }; + + // Immutable after construction + Comparator const compare_; + Arena* const arena_; // Arena used for allocations of nodes + + Node* const head_; + + // Modified only by Insert(). Read racily by readers, but stale + // values are ok. + port::AtomicPointer max_height_; // Height of the entire list + + inline int GetMaxHeight() const { + return static_cast<int>( + reinterpret_cast<intptr_t>(max_height_.NoBarrier_Load())); + } + + // Read/written only by Insert(). + Random rnd_; + + Node* NewNode(const Key& key, int height); + int RandomHeight(); + bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); } + + // Return true if key is greater than the data stored in "n" + bool KeyIsAfterNode(const Key& key, Node* n) const; + + // Return the earliest node that comes at or after key. + // Return NULL if there is no such node. + // + // If prev is non-NULL, fills prev[level] with pointer to previous + // node at "level" for every level in [0..max_height_-1]. + Node* FindGreaterOrEqual(const Key& key, Node** prev) const; + + // Return the latest node with a key < key. + // Return head_ if there is no such node. + Node* FindLessThan(const Key& key) const; + + // Return the last node in the list. + // Return head_ if list is empty. + Node* FindLast() const; + + // No copying allowed + SkipList(const SkipList&); + void operator=(const SkipList&); +}; + +// Implementation details follow +template<typename Key, class Comparator> +struct SkipList<Key,Comparator>::Node { + explicit Node(const Key& k) : key(k) { } + + Key const key; + + // Accessors/mutators for links. Wrapped in methods so we can + // add the appropriate barriers as necessary. + Node* Next(int n) { + assert(n >= 0); + // Use an 'acquire load' so that we observe a fully initialized + // version of the returned Node. + return reinterpret_cast<Node*>(next_[n].Acquire_Load()); + } + void SetNext(int n, Node* x) { + assert(n >= 0); + // Use a 'release store' so that anybody who reads through this + // pointer observes a fully initialized version of the inserted node. + next_[n].Release_Store(x); + } + + // No-barrier variants that can be safely used in a few locations. + Node* NoBarrier_Next(int n) { + assert(n >= 0); + return reinterpret_cast<Node*>(next_[n].NoBarrier_Load()); + } + void NoBarrier_SetNext(int n, Node* x) { + assert(n >= 0); + next_[n].NoBarrier_Store(x); + } + + private: + // Array of length equal to the node height. next_[0] is lowest level link. + port::AtomicPointer next_[1]; +}; + +template<typename Key, class Comparator> +typename SkipList<Key,Comparator>::Node* +SkipList<Key,Comparator>::NewNode(const Key& key, int height) { + char* mem = arena_->AllocateAligned( + sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1)); + return new (mem) Node(key); +} + +template<typename Key, class Comparator> +inline SkipList<Key,Comparator>::Iterator::Iterator(const SkipList* list) { + list_ = list; + node_ = NULL; +} + +template<typename Key, class Comparator> +inline bool SkipList<Key,Comparator>::Iterator::Valid() const { + return node_ != NULL; +} + +template<typename Key, class Comparator> +inline const Key& SkipList<Key,Comparator>::Iterator::key() const { + assert(Valid()); + return node_->key; +} + +template<typename Key, class Comparator> +inline void SkipList<Key,Comparator>::Iterator::Next() { + assert(Valid()); + node_ = node_->Next(0); +} + +template<typename Key, class Comparator> +inline void SkipList<Key,Comparator>::Iterator::Prev() { + // Instead of using explicit "prev" links, we just search for the + // last node that falls before key. + assert(Valid()); + node_ = list_->FindLessThan(node_->key); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template<typename Key, class Comparator> +inline void SkipList<Key,Comparator>::Iterator::Seek(const Key& target) { + node_ = list_->FindGreaterOrEqual(target, NULL); +} + +template<typename Key, class Comparator> +inline void SkipList<Key,Comparator>::Iterator::SeekToFirst() { + node_ = list_->head_->Next(0); +} + +template<typename Key, class Comparator> +inline void SkipList<Key,Comparator>::Iterator::SeekToLast() { + node_ = list_->FindLast(); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template<typename Key, class Comparator> +int SkipList<Key,Comparator>::RandomHeight() { + // Increase height with probability 1 in kBranching + static const unsigned int kBranching = 4; + int height = 1; + while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) { + height++; + } + assert(height > 0); + assert(height <= kMaxHeight); + return height; +} + +template<typename Key, class Comparator> +bool SkipList<Key,Comparator>::KeyIsAfterNode(const Key& key, Node* n) const { + // NULL n is considered infinite + return (n != NULL) && (compare_(n->key, key) < 0); +} + +template<typename Key, class Comparator> +typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindGreaterOrEqual(const Key& key, Node** prev) + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (KeyIsAfterNode(key, next)) { + // Keep searching in this list + x = next; + } else { + if (prev != NULL) prev[level] = x; + if (level == 0) { + return next; + } else { + // Switch to next list + level--; + } + } + } +} + +template<typename Key, class Comparator> +typename SkipList<Key,Comparator>::Node* +SkipList<Key,Comparator>::FindLessThan(const Key& key) const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + assert(x == head_ || compare_(x->key, key) < 0); + Node* next = x->Next(level); + if (next == NULL || compare_(next->key, key) >= 0) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template<typename Key, class Comparator> +typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindLast() + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (next == NULL) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template<typename Key, class Comparator> +SkipList<Key,Comparator>::SkipList(Comparator cmp, Arena* arena) + : compare_(cmp), + arena_(arena), + head_(NewNode(0 /* any key will do */, kMaxHeight)), + max_height_(reinterpret_cast<void*>(1)), + rnd_(0xdeadbeef) { + for (int i = 0; i < kMaxHeight; i++) { + head_->SetNext(i, NULL); + } +} + +template<typename Key, class Comparator> +void SkipList<Key,Comparator>::Insert(const Key& key) { + // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() + // here since Insert() is externally synchronized. + Node* prev[kMaxHeight]; + Node* x = FindGreaterOrEqual(key, prev); + + // Our data structure does not allow duplicate insertion + assert(x == NULL || !Equal(key, x->key)); + + int height = RandomHeight(); + if (height > GetMaxHeight()) { + for (int i = GetMaxHeight(); i < height; i++) { + prev[i] = head_; + } + //fprintf(stderr, "Change height from %d to %d\n", max_height_, height); + + // It is ok to mutate max_height_ without any synchronization + // with concurrent readers. A concurrent reader that observes + // the new value of max_height_ will see either the old value of + // new level pointers from head_ (NULL), or a new value set in + // the loop below. In the former case the reader will + // immediately drop to the next level since NULL sorts after all + // keys. In the latter case the reader will use the new node. + max_height_.NoBarrier_Store(reinterpret_cast<void*>(height)); + } + + x = NewNode(key, height); + for (int i = 0; i < height; i++) { + // NoBarrier_SetNext() suffices since we will add a barrier when + // we publish a pointer to "x" in prev[i]. + x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i)); + prev[i]->SetNext(i, x); + } +} + +template<typename Key, class Comparator> +bool SkipList<Key,Comparator>::Contains(const Key& key) const { + Node* x = FindGreaterOrEqual(key, NULL); + if (x != NULL && Equal(key, x->key)) { + return true; + } else { + return false; + } +} + +} // namespace leveldb diff --git a/src/leveldb/db/skiplist_test.cc b/src/leveldb/db/skiplist_test.cc new file mode 100644 index 0000000000..c78f4b4fb1 --- /dev/null +++ b/src/leveldb/db/skiplist_test.cc @@ -0,0 +1,378 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/skiplist.h" +#include <set> +#include "leveldb/env.h" +#include "util/arena.h" +#include "util/hash.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +typedef uint64_t Key; + +struct Comparator { + int operator()(const Key& a, const Key& b) const { + if (a < b) { + return -1; + } else if (a > b) { + return +1; + } else { + return 0; + } + } +}; + +class SkipTest { }; + +TEST(SkipTest, Empty) { + Arena arena; + Comparator cmp; + SkipList<Key, Comparator> list(cmp, &arena); + ASSERT_TRUE(!list.Contains(10)); + + SkipList<Key, Comparator>::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToFirst(); + ASSERT_TRUE(!iter.Valid()); + iter.Seek(100); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToLast(); + ASSERT_TRUE(!iter.Valid()); +} + +TEST(SkipTest, InsertAndLookup) { + const int N = 2000; + const int R = 5000; + Random rnd(1000); + std::set<Key> keys; + Arena arena; + Comparator cmp; + SkipList<Key, Comparator> list(cmp, &arena); + for (int i = 0; i < N; i++) { + Key key = rnd.Next() % R; + if (keys.insert(key).second) { + list.Insert(key); + } + } + + for (int i = 0; i < R; i++) { + if (list.Contains(i)) { + ASSERT_EQ(keys.count(i), 1); + } else { + ASSERT_EQ(keys.count(i), 0); + } + } + + // Simple iterator tests + { + SkipList<Key, Comparator>::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + + iter.Seek(0); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToFirst(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToLast(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.rbegin()), iter.key()); + } + + // Forward iteration test + for (int i = 0; i < R; i++) { + SkipList<Key, Comparator>::Iterator iter(&list); + iter.Seek(i); + + // Compare against model iterator + std::set<Key>::iterator model_iter = keys.lower_bound(i); + for (int j = 0; j < 3; j++) { + if (model_iter == keys.end()) { + ASSERT_TRUE(!iter.Valid()); + break; + } else { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + ++model_iter; + iter.Next(); + } + } + } + + // Backward iteration test + { + SkipList<Key, Comparator>::Iterator iter(&list); + iter.SeekToLast(); + + // Compare against model iterator + for (std::set<Key>::reverse_iterator model_iter = keys.rbegin(); + model_iter != keys.rend(); + ++model_iter) { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + iter.Prev(); + } + ASSERT_TRUE(!iter.Valid()); + } +} + +// We want to make sure that with a single writer and multiple +// concurrent readers (with no synchronization other than when a +// reader's iterator is created), the reader always observes all the +// data that was present in the skip list when the iterator was +// constructor. Because insertions are happening concurrently, we may +// also observe new values that were inserted since the iterator was +// constructed, but we should never miss any values that were present +// at iterator construction time. +// +// We generate multi-part keys: +// <key,gen,hash> +// where: +// key is in range [0..K-1] +// gen is a generation number for key +// hash is hash(key,gen) +// +// The insertion code picks a random key, sets gen to be 1 + the last +// generation number inserted for that key, and sets hash to Hash(key,gen). +// +// At the beginning of a read, we snapshot the last inserted +// generation number for each key. We then iterate, including random +// calls to Next() and Seek(). For every key we encounter, we +// check that it is either expected given the initial snapshot or has +// been concurrently added since the iterator started. +class ConcurrentTest { + private: + static const uint32_t K = 4; + + static uint64_t key(Key key) { return (key >> 40); } + static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; } + static uint64_t hash(Key key) { return key & 0xff; } + + static uint64_t HashNumbers(uint64_t k, uint64_t g) { + uint64_t data[2] = { k, g }; + return Hash(reinterpret_cast<char*>(data), sizeof(data), 0); + } + + static Key MakeKey(uint64_t k, uint64_t g) { + assert(sizeof(Key) == sizeof(uint64_t)); + assert(k <= K); // We sometimes pass K to seek to the end of the skiplist + assert(g <= 0xffffffffu); + return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff)); + } + + static bool IsValidKey(Key k) { + return hash(k) == (HashNumbers(key(k), gen(k)) & 0xff); + } + + static Key RandomTarget(Random* rnd) { + switch (rnd->Next() % 10) { + case 0: + // Seek to beginning + return MakeKey(0, 0); + case 1: + // Seek to end + return MakeKey(K, 0); + default: + // Seek to middle + return MakeKey(rnd->Next() % K, 0); + } + } + + // Per-key generation + struct State { + port::AtomicPointer generation[K]; + void Set(int k, intptr_t v) { + generation[k].Release_Store(reinterpret_cast<void*>(v)); + } + intptr_t Get(int k) { + return reinterpret_cast<intptr_t>(generation[k].Acquire_Load()); + } + + State() { + for (int k = 0; k < K; k++) { + Set(k, 0); + } + } + }; + + // Current state of the test + State current_; + + Arena arena_; + + // SkipList is not protected by mu_. We just use a single writer + // thread to modify it. + SkipList<Key, Comparator> list_; + + public: + ConcurrentTest() : list_(Comparator(), &arena_) { } + + // REQUIRES: External synchronization + void WriteStep(Random* rnd) { + const uint32_t k = rnd->Next() % K; + const intptr_t g = current_.Get(k) + 1; + const Key key = MakeKey(k, g); + list_.Insert(key); + current_.Set(k, g); + } + + void ReadStep(Random* rnd) { + // Remember the initial committed state of the skiplist. + State initial_state; + for (int k = 0; k < K; k++) { + initial_state.Set(k, current_.Get(k)); + } + + Key pos = RandomTarget(rnd); + SkipList<Key, Comparator>::Iterator iter(&list_); + iter.Seek(pos); + while (true) { + Key current; + if (!iter.Valid()) { + current = MakeKey(K, 0); + } else { + current = iter.key(); + ASSERT_TRUE(IsValidKey(current)) << current; + } + ASSERT_LE(pos, current) << "should not go backwards"; + + // Verify that everything in [pos,current) was not present in + // initial_state. + while (pos < current) { + ASSERT_LT(key(pos), K) << pos; + + // Note that generation 0 is never inserted, so it is ok if + // <*,0,*> is missing. + ASSERT_TRUE((gen(pos) == 0) || + (gen(pos) > initial_state.Get(key(pos))) + ) << "key: " << key(pos) + << "; gen: " << gen(pos) + << "; initgen: " + << initial_state.Get(key(pos)); + + // Advance to next key in the valid key space + if (key(pos) < key(current)) { + pos = MakeKey(key(pos) + 1, 0); + } else { + pos = MakeKey(key(pos), gen(pos) + 1); + } + } + + if (!iter.Valid()) { + break; + } + + if (rnd->Next() % 2) { + iter.Next(); + pos = MakeKey(key(pos), gen(pos) + 1); + } else { + Key new_target = RandomTarget(rnd); + if (new_target > pos) { + pos = new_target; + iter.Seek(new_target); + } + } + } + } +}; +const uint32_t ConcurrentTest::K; + +// Simple test that does single-threaded testing of the ConcurrentTest +// scaffolding. +TEST(SkipTest, ConcurrentWithoutThreads) { + ConcurrentTest test; + Random rnd(test::RandomSeed()); + for (int i = 0; i < 10000; i++) { + test.ReadStep(&rnd); + test.WriteStep(&rnd); + } +} + +class TestState { + public: + ConcurrentTest t_; + int seed_; + port::AtomicPointer quit_flag_; + + enum ReaderState { + STARTING, + RUNNING, + DONE + }; + + explicit TestState(int s) + : seed_(s), + quit_flag_(NULL), + state_(STARTING), + state_cv_(&mu_) {} + + void Wait(ReaderState s) { + mu_.Lock(); + while (state_ != s) { + state_cv_.Wait(); + } + mu_.Unlock(); + } + + void Change(ReaderState s) { + mu_.Lock(); + state_ = s; + state_cv_.Signal(); + mu_.Unlock(); + } + + private: + port::Mutex mu_; + ReaderState state_; + port::CondVar state_cv_; +}; + +static void ConcurrentReader(void* arg) { + TestState* state = reinterpret_cast<TestState*>(arg); + Random rnd(state->seed_); + int64_t reads = 0; + state->Change(TestState::RUNNING); + while (!state->quit_flag_.Acquire_Load()) { + state->t_.ReadStep(&rnd); + ++reads; + } + state->Change(TestState::DONE); +} + +static void RunConcurrent(int run) { + const int seed = test::RandomSeed() + (run * 100); + Random rnd(seed); + const int N = 1000; + const int kSize = 1000; + for (int i = 0; i < N; i++) { + if ((i % 100) == 0) { + fprintf(stderr, "Run %d of %d\n", i, N); + } + TestState state(seed + 1); + Env::Default()->Schedule(ConcurrentReader, &state); + state.Wait(TestState::RUNNING); + for (int i = 0; i < kSize; i++) { + state.t_.WriteStep(&rnd); + } + state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do + state.Wait(TestState::DONE); + } +} + +TEST(SkipTest, Concurrent1) { RunConcurrent(1); } +TEST(SkipTest, Concurrent2) { RunConcurrent(2); } +TEST(SkipTest, Concurrent3) { RunConcurrent(3); } +TEST(SkipTest, Concurrent4) { RunConcurrent(4); } +TEST(SkipTest, Concurrent5) { RunConcurrent(5); } + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/snapshot.h b/src/leveldb/db/snapshot.h new file mode 100644 index 0000000000..e7f8fd2c37 --- /dev/null +++ b/src/leveldb/db/snapshot.h @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_ +#define STORAGE_LEVELDB_DB_SNAPSHOT_H_ + +#include "leveldb/db.h" + +namespace leveldb { + +class SnapshotList; + +// Snapshots are kept in a doubly-linked list in the DB. +// Each SnapshotImpl corresponds to a particular sequence number. +class SnapshotImpl : public Snapshot { + public: + SequenceNumber number_; // const after creation + + private: + friend class SnapshotList; + + // SnapshotImpl is kept in a doubly-linked circular list + SnapshotImpl* prev_; + SnapshotImpl* next_; + + SnapshotList* list_; // just for sanity checks +}; + +class SnapshotList { + public: + SnapshotList() { + list_.prev_ = &list_; + list_.next_ = &list_; + } + + bool empty() const { return list_.next_ == &list_; } + SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; } + SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; } + + const SnapshotImpl* New(SequenceNumber seq) { + SnapshotImpl* s = new SnapshotImpl; + s->number_ = seq; + s->list_ = this; + s->next_ = &list_; + s->prev_ = list_.prev_; + s->prev_->next_ = s; + s->next_->prev_ = s; + return s; + } + + void Delete(const SnapshotImpl* s) { + assert(s->list_ == this); + s->prev_->next_ = s->next_; + s->next_->prev_ = s->prev_; + delete s; + } + + private: + // Dummy head of doubly-linked list of snapshots + SnapshotImpl list_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_SNAPSHOT_H_ diff --git a/src/leveldb/db/table_cache.cc b/src/leveldb/db/table_cache.cc new file mode 100644 index 0000000000..497db27076 --- /dev/null +++ b/src/leveldb/db/table_cache.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/table_cache.h" + +#include "db/filename.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/coding.h" + +namespace leveldb { + +struct TableAndFile { + RandomAccessFile* file; + Table* table; +}; + +static void DeleteEntry(const Slice& key, void* value) { + TableAndFile* tf = reinterpret_cast<TableAndFile*>(value); + delete tf->table; + delete tf->file; + delete tf; +} + +static void UnrefEntry(void* arg1, void* arg2) { + Cache* cache = reinterpret_cast<Cache*>(arg1); + Cache::Handle* h = reinterpret_cast<Cache::Handle*>(arg2); + cache->Release(h); +} + +TableCache::TableCache(const std::string& dbname, + const Options* options, + int entries) + : env_(options->env), + dbname_(dbname), + options_(options), + cache_(NewLRUCache(entries)) { +} + +TableCache::~TableCache() { + delete cache_; +} + +Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, + Cache::Handle** handle) { + Status s; + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + Slice key(buf, sizeof(buf)); + *handle = cache_->Lookup(key); + if (*handle == NULL) { + std::string fname = TableFileName(dbname_, file_number); + RandomAccessFile* file = NULL; + Table* table = NULL; + s = env_->NewRandomAccessFile(fname, &file); + if (s.ok()) { + s = Table::Open(*options_, file, file_size, &table); + } + + if (!s.ok()) { + assert(table == NULL); + delete file; + // We do not cache error results so that if the error is transient, + // or somebody repairs the file, we recover automatically. + } else { + TableAndFile* tf = new TableAndFile; + tf->file = file; + tf->table = table; + *handle = cache_->Insert(key, tf, 1, &DeleteEntry); + } + } + return s; +} + +Iterator* TableCache::NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr) { + if (tableptr != NULL) { + *tableptr = NULL; + } + + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (!s.ok()) { + return NewErrorIterator(s); + } + + Table* table = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table; + Iterator* result = table->NewIterator(options); + result->RegisterCleanup(&UnrefEntry, cache_, handle); + if (tableptr != NULL) { + *tableptr = table; + } + return result; +} + +Status TableCache::Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (s.ok()) { + Table* t = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table; + s = t->InternalGet(options, k, arg, saver); + cache_->Release(handle); + } + return s; +} + +void TableCache::Evict(uint64_t file_number) { + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + cache_->Erase(Slice(buf, sizeof(buf))); +} + +} // namespace leveldb diff --git a/src/leveldb/db/table_cache.h b/src/leveldb/db/table_cache.h new file mode 100644 index 0000000000..8cf4aaf12d --- /dev/null +++ b/src/leveldb/db/table_cache.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread-safe (provides internal synchronization) + +#ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_ +#define STORAGE_LEVELDB_DB_TABLE_CACHE_H_ + +#include <string> +#include <stdint.h> +#include "db/dbformat.h" +#include "leveldb/cache.h" +#include "leveldb/table.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +class TableCache { + public: + TableCache(const std::string& dbname, const Options* options, int entries); + ~TableCache(); + + // Return an iterator for the specified file number (the corresponding + // file length must be exactly "file_size" bytes). If "tableptr" is + // non-NULL, also sets "*tableptr" to point to the Table object + // underlying the returned iterator, or NULL if no Table object underlies + // the returned iterator. The returned "*tableptr" object is owned by + // the cache and should not be deleted, and is valid for as long as the + // returned iterator is live. + Iterator* NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr = NULL); + + // If a seek to internal key "k" in specified file finds an entry, + // call (*handle_result)(arg, found_key, found_value). + Status Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*handle_result)(void*, const Slice&, const Slice&)); + + // Evict any entry for the specified file number + void Evict(uint64_t file_number); + + private: + Env* const env_; + const std::string dbname_; + const Options* options_; + Cache* cache_; + + Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_TABLE_CACHE_H_ diff --git a/src/leveldb/db/version_edit.cc b/src/leveldb/db/version_edit.cc new file mode 100644 index 0000000000..f10a2d58b2 --- /dev/null +++ b/src/leveldb/db/version_edit.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" + +#include "db/version_set.h" +#include "util/coding.h" + +namespace leveldb { + +// Tag numbers for serialized VersionEdit. These numbers are written to +// disk and should not be changed. +enum Tag { + kComparator = 1, + kLogNumber = 2, + kNextFileNumber = 3, + kLastSequence = 4, + kCompactPointer = 5, + kDeletedFile = 6, + kNewFile = 7, + // 8 was used for large value refs + kPrevLogNumber = 9 +}; + +void VersionEdit::Clear() { + comparator_.clear(); + log_number_ = 0; + prev_log_number_ = 0; + last_sequence_ = 0; + next_file_number_ = 0; + has_comparator_ = false; + has_log_number_ = false; + has_prev_log_number_ = false; + has_next_file_number_ = false; + has_last_sequence_ = false; + deleted_files_.clear(); + new_files_.clear(); +} + +void VersionEdit::EncodeTo(std::string* dst) const { + if (has_comparator_) { + PutVarint32(dst, kComparator); + PutLengthPrefixedSlice(dst, comparator_); + } + if (has_log_number_) { + PutVarint32(dst, kLogNumber); + PutVarint64(dst, log_number_); + } + if (has_prev_log_number_) { + PutVarint32(dst, kPrevLogNumber); + PutVarint64(dst, prev_log_number_); + } + if (has_next_file_number_) { + PutVarint32(dst, kNextFileNumber); + PutVarint64(dst, next_file_number_); + } + if (has_last_sequence_) { + PutVarint32(dst, kLastSequence); + PutVarint64(dst, last_sequence_); + } + + for (size_t i = 0; i < compact_pointers_.size(); i++) { + PutVarint32(dst, kCompactPointer); + PutVarint32(dst, compact_pointers_[i].first); // level + PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode()); + } + + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + PutVarint32(dst, kDeletedFile); + PutVarint32(dst, iter->first); // level + PutVarint64(dst, iter->second); // file number + } + + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + PutVarint32(dst, kNewFile); + PutVarint32(dst, new_files_[i].first); // level + PutVarint64(dst, f.number); + PutVarint64(dst, f.file_size); + PutLengthPrefixedSlice(dst, f.smallest.Encode()); + PutLengthPrefixedSlice(dst, f.largest.Encode()); + } +} + +static bool GetInternalKey(Slice* input, InternalKey* dst) { + Slice str; + if (GetLengthPrefixedSlice(input, &str)) { + dst->DecodeFrom(str); + return true; + } else { + return false; + } +} + +static bool GetLevel(Slice* input, int* level) { + uint32_t v; + if (GetVarint32(input, &v) && + v < config::kNumLevels) { + *level = v; + return true; + } else { + return false; + } +} + +Status VersionEdit::DecodeFrom(const Slice& src) { + Clear(); + Slice input = src; + const char* msg = NULL; + uint32_t tag; + + // Temporary storage for parsing + int level; + uint64_t number; + FileMetaData f; + Slice str; + InternalKey key; + + while (msg == NULL && GetVarint32(&input, &tag)) { + switch (tag) { + case kComparator: + if (GetLengthPrefixedSlice(&input, &str)) { + comparator_ = str.ToString(); + has_comparator_ = true; + } else { + msg = "comparator name"; + } + break; + + case kLogNumber: + if (GetVarint64(&input, &log_number_)) { + has_log_number_ = true; + } else { + msg = "log number"; + } + break; + + case kPrevLogNumber: + if (GetVarint64(&input, &prev_log_number_)) { + has_prev_log_number_ = true; + } else { + msg = "previous log number"; + } + break; + + case kNextFileNumber: + if (GetVarint64(&input, &next_file_number_)) { + has_next_file_number_ = true; + } else { + msg = "next file number"; + } + break; + + case kLastSequence: + if (GetVarint64(&input, &last_sequence_)) { + has_last_sequence_ = true; + } else { + msg = "last sequence number"; + } + break; + + case kCompactPointer: + if (GetLevel(&input, &level) && + GetInternalKey(&input, &key)) { + compact_pointers_.push_back(std::make_pair(level, key)); + } else { + msg = "compaction pointer"; + } + break; + + case kDeletedFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &number)) { + deleted_files_.insert(std::make_pair(level, number)); + } else { + msg = "deleted file"; + } + break; + + case kNewFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &f.number) && + GetVarint64(&input, &f.file_size) && + GetInternalKey(&input, &f.smallest) && + GetInternalKey(&input, &f.largest)) { + new_files_.push_back(std::make_pair(level, f)); + } else { + msg = "new-file entry"; + } + break; + + default: + msg = "unknown tag"; + break; + } + } + + if (msg == NULL && !input.empty()) { + msg = "invalid tag"; + } + + Status result; + if (msg != NULL) { + result = Status::Corruption("VersionEdit", msg); + } + return result; +} + +std::string VersionEdit::DebugString() const { + std::string r; + r.append("VersionEdit {"); + if (has_comparator_) { + r.append("\n Comparator: "); + r.append(comparator_); + } + if (has_log_number_) { + r.append("\n LogNumber: "); + AppendNumberTo(&r, log_number_); + } + if (has_prev_log_number_) { + r.append("\n PrevLogNumber: "); + AppendNumberTo(&r, prev_log_number_); + } + if (has_next_file_number_) { + r.append("\n NextFile: "); + AppendNumberTo(&r, next_file_number_); + } + if (has_last_sequence_) { + r.append("\n LastSeq: "); + AppendNumberTo(&r, last_sequence_); + } + for (size_t i = 0; i < compact_pointers_.size(); i++) { + r.append("\n CompactPointer: "); + AppendNumberTo(&r, compact_pointers_[i].first); + r.append(" "); + r.append(compact_pointers_[i].second.DebugString()); + } + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + r.append("\n DeleteFile: "); + AppendNumberTo(&r, iter->first); + r.append(" "); + AppendNumberTo(&r, iter->second); + } + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + r.append("\n AddFile: "); + AppendNumberTo(&r, new_files_[i].first); + r.append(" "); + AppendNumberTo(&r, f.number); + r.append(" "); + AppendNumberTo(&r, f.file_size); + r.append(" "); + r.append(f.smallest.DebugString()); + r.append(" .. "); + r.append(f.largest.DebugString()); + } + r.append("\n}\n"); + return r; +} + +} // namespace leveldb diff --git a/src/leveldb/db/version_edit.h b/src/leveldb/db/version_edit.h new file mode 100644 index 0000000000..eaef77b327 --- /dev/null +++ b/src/leveldb/db/version_edit.h @@ -0,0 +1,107 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_VERSION_EDIT_H_ +#define STORAGE_LEVELDB_DB_VERSION_EDIT_H_ + +#include <set> +#include <utility> +#include <vector> +#include "db/dbformat.h" + +namespace leveldb { + +class VersionSet; + +struct FileMetaData { + int refs; + int allowed_seeks; // Seeks allowed until compaction + uint64_t number; + uint64_t file_size; // File size in bytes + InternalKey smallest; // Smallest internal key served by table + InternalKey largest; // Largest internal key served by table + + FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { } +}; + +class VersionEdit { + public: + VersionEdit() { Clear(); } + ~VersionEdit() { } + + void Clear(); + + void SetComparatorName(const Slice& name) { + has_comparator_ = true; + comparator_ = name.ToString(); + } + void SetLogNumber(uint64_t num) { + has_log_number_ = true; + log_number_ = num; + } + void SetPrevLogNumber(uint64_t num) { + has_prev_log_number_ = true; + prev_log_number_ = num; + } + void SetNextFile(uint64_t num) { + has_next_file_number_ = true; + next_file_number_ = num; + } + void SetLastSequence(SequenceNumber seq) { + has_last_sequence_ = true; + last_sequence_ = seq; + } + void SetCompactPointer(int level, const InternalKey& key) { + compact_pointers_.push_back(std::make_pair(level, key)); + } + + // Add the specified file at the specified number. + // REQUIRES: This version has not been saved (see VersionSet::SaveTo) + // REQUIRES: "smallest" and "largest" are smallest and largest keys in file + void AddFile(int level, uint64_t file, + uint64_t file_size, + const InternalKey& smallest, + const InternalKey& largest) { + FileMetaData f; + f.number = file; + f.file_size = file_size; + f.smallest = smallest; + f.largest = largest; + new_files_.push_back(std::make_pair(level, f)); + } + + // Delete the specified "file" from the specified "level". + void DeleteFile(int level, uint64_t file) { + deleted_files_.insert(std::make_pair(level, file)); + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(const Slice& src); + + std::string DebugString() const; + + private: + friend class VersionSet; + + typedef std::set< std::pair<int, uint64_t> > DeletedFileSet; + + std::string comparator_; + uint64_t log_number_; + uint64_t prev_log_number_; + uint64_t next_file_number_; + SequenceNumber last_sequence_; + bool has_comparator_; + bool has_log_number_; + bool has_prev_log_number_; + bool has_next_file_number_; + bool has_last_sequence_; + + std::vector< std::pair<int, InternalKey> > compact_pointers_; + DeletedFileSet deleted_files_; + std::vector< std::pair<int, FileMetaData> > new_files_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_EDIT_H_ diff --git a/src/leveldb/db/version_edit_test.cc b/src/leveldb/db/version_edit_test.cc new file mode 100644 index 0000000000..280310b49d --- /dev/null +++ b/src/leveldb/db/version_edit_test.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" +#include "util/testharness.h" + +namespace leveldb { + +static void TestEncodeDecode(const VersionEdit& edit) { + std::string encoded, encoded2; + edit.EncodeTo(&encoded); + VersionEdit parsed; + Status s = parsed.DecodeFrom(encoded); + ASSERT_TRUE(s.ok()) << s.ToString(); + parsed.EncodeTo(&encoded2); + ASSERT_EQ(encoded, encoded2); +} + +class VersionEditTest { }; + +TEST(VersionEditTest, EncodeDecode) { + static const uint64_t kBig = 1ull << 50; + + VersionEdit edit; + for (int i = 0; i < 4; i++) { + TestEncodeDecode(edit); + edit.AddFile(3, kBig + 300 + i, kBig + 400 + i, + InternalKey("foo", kBig + 500 + i, kTypeValue), + InternalKey("zoo", kBig + 600 + i, kTypeDeletion)); + edit.DeleteFile(4, kBig + 700 + i); + edit.SetCompactPointer(i, InternalKey("x", kBig + 900 + i, kTypeValue)); + } + + edit.SetComparatorName("foo"); + edit.SetLogNumber(kBig + 100); + edit.SetNextFile(kBig + 200); + edit.SetLastSequence(kBig + 1000); + TestEncodeDecode(edit); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc new file mode 100644 index 0000000000..cf976b437e --- /dev/null +++ b/src/leveldb/db/version_set.cc @@ -0,0 +1,1402 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" + +#include <algorithm> +#include <stdio.h> +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "leveldb/env.h" +#include "leveldb/table_builder.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +static const int kTargetFileSize = 2 * 1048576; + +// Maximum bytes of overlaps in grandparent (i.e., level+2) before we +// stop building a single file in a level->level+1 compaction. +static const int64_t kMaxGrandParentOverlapBytes = 10 * kTargetFileSize; + +// Maximum number of bytes in all compacted files. We avoid expanding +// the lower level file set of a compaction if it would make the +// total compaction cover more than this many bytes. +static const int64_t kExpandedCompactionByteSizeLimit = 25 * kTargetFileSize; + +static double MaxBytesForLevel(int level) { + // Note: the result for level zero is not really used since we set + // the level-0 compaction threshold based on number of files. + double result = 10 * 1048576.0; // Result for both level-0 and level-1 + while (level > 1) { + result *= 10; + level--; + } + return result; +} + +static uint64_t MaxFileSizeForLevel(int level) { + return kTargetFileSize; // We could vary per level to reduce number of files? +} + +static int64_t TotalFileSize(const std::vector<FileMetaData*>& files) { + int64_t sum = 0; + for (size_t i = 0; i < files.size(); i++) { + sum += files[i]->file_size; + } + return sum; +} + +namespace { +std::string IntSetToString(const std::set<uint64_t>& s) { + std::string result = "{"; + for (std::set<uint64_t>::const_iterator it = s.begin(); + it != s.end(); + ++it) { + result += (result.size() > 1) ? "," : ""; + result += NumberToString(*it); + } + result += "}"; + return result; +} +} // namespace + +Version::~Version() { + assert(refs_ == 0); + + // Remove from linked list + prev_->next_ = next_; + next_->prev_ = prev_; + + // Drop references to files + for (int level = 0; level < config::kNumLevels; level++) { + for (size_t i = 0; i < files_[level].size(); i++) { + FileMetaData* f = files_[level][i]; + assert(f->refs > 0); + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } +} + +int FindFile(const InternalKeyComparator& icmp, + const std::vector<FileMetaData*>& files, + const Slice& key) { + uint32_t left = 0; + uint32_t right = files.size(); + while (left < right) { + uint32_t mid = (left + right) / 2; + const FileMetaData* f = files[mid]; + if (icmp.InternalKeyComparator::Compare(f->largest.Encode(), key) < 0) { + // Key at "mid.largest" is < "target". Therefore all + // files at or before "mid" are uninteresting. + left = mid + 1; + } else { + // Key at "mid.largest" is >= "target". Therefore all files + // after "mid" are uninteresting. + right = mid; + } + } + return right; +} + +static bool AfterFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs before all keys and is therefore never after *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->largest.user_key()) > 0); +} + +static bool BeforeFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs after all keys and is therefore never before *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->smallest.user_key()) < 0); +} + +bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector<FileMetaData*>& files, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + const Comparator* ucmp = icmp.user_comparator(); + if (!disjoint_sorted_files) { + // Need to check against all files + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + if (AfterFile(ucmp, smallest_user_key, f) || + BeforeFile(ucmp, largest_user_key, f)) { + // No overlap + } else { + return true; // Overlap + } + } + return false; + } + + // Binary search over file list + uint32_t index = 0; + if (smallest_user_key != NULL) { + // Find the earliest possible internal key for smallest_user_key + InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); + index = FindFile(icmp, files, small.Encode()); + } + + if (index >= files.size()) { + // beginning of range is after all files, so no overlap. + return false; + } + + return !BeforeFile(ucmp, largest_user_key, files[index]); +} + +// An internal iterator. For a given version/level pair, yields +// information about the files in the level. For a given entry, key() +// is the largest key that occurs in the file, and value() is an +// 16-byte value containing the file number and file size, both +// encoded using EncodeFixed64. +class Version::LevelFileNumIterator : public Iterator { + public: + LevelFileNumIterator(const InternalKeyComparator& icmp, + const std::vector<FileMetaData*>* flist) + : icmp_(icmp), + flist_(flist), + index_(flist->size()) { // Marks as invalid + } + virtual bool Valid() const { + return index_ < flist_->size(); + } + virtual void Seek(const Slice& target) { + index_ = FindFile(icmp_, *flist_, target); + } + virtual void SeekToFirst() { index_ = 0; } + virtual void SeekToLast() { + index_ = flist_->empty() ? 0 : flist_->size() - 1; + } + virtual void Next() { + assert(Valid()); + index_++; + } + virtual void Prev() { + assert(Valid()); + if (index_ == 0) { + index_ = flist_->size(); // Marks as invalid + } else { + index_--; + } + } + Slice key() const { + assert(Valid()); + return (*flist_)[index_]->largest.Encode(); + } + Slice value() const { + assert(Valid()); + EncodeFixed64(value_buf_, (*flist_)[index_]->number); + EncodeFixed64(value_buf_+8, (*flist_)[index_]->file_size); + return Slice(value_buf_, sizeof(value_buf_)); + } + virtual Status status() const { return Status::OK(); } + private: + const InternalKeyComparator icmp_; + const std::vector<FileMetaData*>* const flist_; + uint32_t index_; + + // Backing store for value(). Holds the file number and size. + mutable char value_buf_[16]; +}; + +static Iterator* GetFileIterator(void* arg, + const ReadOptions& options, + const Slice& file_value) { + TableCache* cache = reinterpret_cast<TableCache*>(arg); + if (file_value.size() != 16) { + return NewErrorIterator( + Status::Corruption("FileReader invoked with unexpected value")); + } else { + return cache->NewIterator(options, + DecodeFixed64(file_value.data()), + DecodeFixed64(file_value.data() + 8)); + } +} + +Iterator* Version::NewConcatenatingIterator(const ReadOptions& options, + int level) const { + return NewTwoLevelIterator( + new LevelFileNumIterator(vset_->icmp_, &files_[level]), + &GetFileIterator, vset_->table_cache_, options); +} + +void Version::AddIterators(const ReadOptions& options, + std::vector<Iterator*>* iters) { + // Merge all level zero files together since they may overlap + for (size_t i = 0; i < files_[0].size(); i++) { + iters->push_back( + vset_->table_cache_->NewIterator( + options, files_[0][i]->number, files_[0][i]->file_size)); + } + + // For levels > 0, we can use a concatenating iterator that sequentially + // walks through the non-overlapping files in the level, opening them + // lazily. + for (int level = 1; level < config::kNumLevels; level++) { + if (!files_[level].empty()) { + iters->push_back(NewConcatenatingIterator(options, level)); + } + } +} + +// Callback from TableCache::Get() +namespace { +enum SaverState { + kNotFound, + kFound, + kDeleted, + kCorrupt, +}; +struct Saver { + SaverState state; + const Comparator* ucmp; + Slice user_key; + std::string* value; +}; +} +static void SaveValue(void* arg, const Slice& ikey, const Slice& v) { + Saver* s = reinterpret_cast<Saver*>(arg); + ParsedInternalKey parsed_key; + if (!ParseInternalKey(ikey, &parsed_key)) { + s->state = kCorrupt; + } else { + if (s->ucmp->Compare(parsed_key.user_key, s->user_key) == 0) { + s->state = (parsed_key.type == kTypeValue) ? kFound : kDeleted; + if (s->state == kFound) { + s->value->assign(v.data(), v.size()); + } + } + } +} + +static bool NewestFirst(FileMetaData* a, FileMetaData* b) { + return a->number > b->number; +} + +Status Version::Get(const ReadOptions& options, + const LookupKey& k, + std::string* value, + GetStats* stats) { + Slice ikey = k.internal_key(); + Slice user_key = k.user_key(); + const Comparator* ucmp = vset_->icmp_.user_comparator(); + Status s; + + stats->seek_file = NULL; + stats->seek_file_level = -1; + FileMetaData* last_file_read = NULL; + int last_file_read_level = -1; + + // We can search level-by-level since entries never hop across + // levels. Therefore we are guaranteed that if we find data + // in an smaller level, later levels are irrelevant. + std::vector<FileMetaData*> tmp; + FileMetaData* tmp2; + for (int level = 0; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Get the list of files to search in this level + FileMetaData* const* files = &files_[level][0]; + if (level == 0) { + // Level-0 files may overlap each other. Find all files that + // overlap user_key and process them in order from newest to oldest. + tmp.reserve(num_files); + for (uint32_t i = 0; i < num_files; i++) { + FileMetaData* f = files[i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (tmp.empty()) continue; + + std::sort(tmp.begin(), tmp.end(), NewestFirst); + files = &tmp[0]; + num_files = tmp.size(); + } else { + // Binary search to find earliest index whose largest key >= ikey. + uint32_t index = FindFile(vset_->icmp_, files_[level], ikey); + if (index >= num_files) { + files = NULL; + num_files = 0; + } else { + tmp2 = files[index]; + if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) { + // All of "tmp2" is past any data for user_key + files = NULL; + num_files = 0; + } else { + files = &tmp2; + num_files = 1; + } + } + } + + for (uint32_t i = 0; i < num_files; ++i) { + if (last_file_read != NULL && stats->seek_file == NULL) { + // We have had more than one seek for this read. Charge the 1st file. + stats->seek_file = last_file_read; + stats->seek_file_level = last_file_read_level; + } + + FileMetaData* f = files[i]; + last_file_read = f; + last_file_read_level = level; + + Saver saver; + saver.state = kNotFound; + saver.ucmp = ucmp; + saver.user_key = user_key; + saver.value = value; + s = vset_->table_cache_->Get(options, f->number, f->file_size, + ikey, &saver, SaveValue); + if (!s.ok()) { + return s; + } + switch (saver.state) { + case kNotFound: + break; // Keep searching in other files + case kFound: + return s; + case kDeleted: + s = Status::NotFound(Slice()); // Use empty error message for speed + return s; + case kCorrupt: + s = Status::Corruption("corrupted key for ", user_key); + return s; + } + } + } + + return Status::NotFound(Slice()); // Use an empty error message for speed +} + +bool Version::UpdateStats(const GetStats& stats) { + FileMetaData* f = stats.seek_file; + if (f != NULL) { + f->allowed_seeks--; + if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) { + file_to_compact_ = f; + file_to_compact_level_ = stats.seek_file_level; + return true; + } + } + return false; +} + +void Version::Ref() { + ++refs_; +} + +void Version::Unref() { + assert(this != &vset_->dummy_versions_); + assert(refs_ >= 1); + --refs_; + if (refs_ == 0) { + delete this; + } +} + +bool Version::OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + return SomeFileOverlapsRange(vset_->icmp_, (level > 0), files_[level], + smallest_user_key, largest_user_key); +} + +int Version::PickLevelForMemTableOutput( + const Slice& smallest_user_key, + const Slice& largest_user_key) { + int level = 0; + if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) { + // Push to next level if there is no overlap in next level, + // and the #bytes overlapping in the level after that are limited. + InternalKey start(smallest_user_key, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey limit(largest_user_key, 0, static_cast<ValueType>(0)); + std::vector<FileMetaData*> overlaps; + while (level < config::kMaxMemCompactLevel) { + if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) { + break; + } + GetOverlappingInputs(level + 2, &start, &limit, &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > kMaxGrandParentOverlapBytes) { + break; + } + level++; + } + } + return level; +} + +// Store in "*inputs" all files in "level" that overlap [begin,end] +void Version::GetOverlappingInputs( + int level, + const InternalKey* begin, + const InternalKey* end, + std::vector<FileMetaData*>* inputs) { + inputs->clear(); + Slice user_begin, user_end; + if (begin != NULL) { + user_begin = begin->user_key(); + } + if (end != NULL) { + user_end = end->user_key(); + } + const Comparator* user_cmp = vset_->icmp_.user_comparator(); + for (size_t i = 0; i < files_[level].size(); ) { + FileMetaData* f = files_[level][i++]; + const Slice file_start = f->smallest.user_key(); + const Slice file_limit = f->largest.user_key(); + if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) { + // "f" is completely before specified range; skip it + } else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) { + // "f" is completely after specified range; skip it + } else { + inputs->push_back(f); + if (level == 0) { + // Level-0 files may overlap each other. So check if the newly + // added file has expanded the range. If so, restart search. + if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) { + user_begin = file_start; + inputs->clear(); + i = 0; + } else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) { + user_end = file_limit; + inputs->clear(); + i = 0; + } + } + } + } +} + +std::string Version::DebugString() const { + std::string r; + for (int level = 0; level < config::kNumLevels; level++) { + // E.g., + // --- level 1 --- + // 17:123['a' .. 'd'] + // 20:43['e' .. 'g'] + r.append("--- level "); + AppendNumberTo(&r, level); + r.append(" ---\n"); + const std::vector<FileMetaData*>& files = files_[level]; + for (size_t i = 0; i < files.size(); i++) { + r.push_back(' '); + AppendNumberTo(&r, files[i]->number); + r.push_back(':'); + AppendNumberTo(&r, files[i]->file_size); + r.append("["); + r.append(files[i]->smallest.DebugString()); + r.append(" .. "); + r.append(files[i]->largest.DebugString()); + r.append("]\n"); + } + } + return r; +} + +// A helper class so we can efficiently apply a whole sequence +// of edits to a particular state without creating intermediate +// Versions that contain full copies of the intermediate state. +class VersionSet::Builder { + private: + // Helper to sort by v->files_[file_number].smallest + struct BySmallestKey { + const InternalKeyComparator* internal_comparator; + + bool operator()(FileMetaData* f1, FileMetaData* f2) const { + int r = internal_comparator->Compare(f1->smallest, f2->smallest); + if (r != 0) { + return (r < 0); + } else { + // Break ties by file number + return (f1->number < f2->number); + } + } + }; + + typedef std::set<FileMetaData*, BySmallestKey> FileSet; + struct LevelState { + std::set<uint64_t> deleted_files; + FileSet* added_files; + }; + + VersionSet* vset_; + Version* base_; + LevelState levels_[config::kNumLevels]; + + public: + // Initialize a builder with the files from *base and other info from *vset + Builder(VersionSet* vset, Version* base) + : vset_(vset), + base_(base) { + base_->Ref(); + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + levels_[level].added_files = new FileSet(cmp); + } + } + + ~Builder() { + for (int level = 0; level < config::kNumLevels; level++) { + const FileSet* added = levels_[level].added_files; + std::vector<FileMetaData*> to_unref; + to_unref.reserve(added->size()); + for (FileSet::const_iterator it = added->begin(); + it != added->end(); ++it) { + to_unref.push_back(*it); + } + delete added; + for (uint32_t i = 0; i < to_unref.size(); i++) { + FileMetaData* f = to_unref[i]; + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } + base_->Unref(); + } + + // Apply all of the edits in *edit to the current state. + void Apply(VersionEdit* edit) { + // Update compaction pointers + for (size_t i = 0; i < edit->compact_pointers_.size(); i++) { + const int level = edit->compact_pointers_[i].first; + vset_->compact_pointer_[level] = + edit->compact_pointers_[i].second.Encode().ToString(); + } + + // Delete files + const VersionEdit::DeletedFileSet& del = edit->deleted_files_; + for (VersionEdit::DeletedFileSet::const_iterator iter = del.begin(); + iter != del.end(); + ++iter) { + const int level = iter->first; + const uint64_t number = iter->second; + levels_[level].deleted_files.insert(number); + } + + // Add new files + for (size_t i = 0; i < edit->new_files_.size(); i++) { + const int level = edit->new_files_[i].first; + FileMetaData* f = new FileMetaData(edit->new_files_[i].second); + f->refs = 1; + + // We arrange to automatically compact this file after + // a certain number of seeks. Let's assume: + // (1) One seek costs 10ms + // (2) Writing or reading 1MB costs 10ms (100MB/s) + // (3) A compaction of 1MB does 25MB of IO: + // 1MB read from this level + // 10-12MB read from next level (boundaries may be misaligned) + // 10-12MB written to next level + // This implies that 25 seeks cost the same as the compaction + // of 1MB of data. I.e., one seek costs approximately the + // same as the compaction of 40KB of data. We are a little + // conservative and allow approximately one seek for every 16KB + // of data before triggering a compaction. + f->allowed_seeks = (f->file_size / 16384); + if (f->allowed_seeks < 100) f->allowed_seeks = 100; + + levels_[level].deleted_files.erase(f->number); + levels_[level].added_files->insert(f); + } + } + + // Save the current state in *v. + void SaveTo(Version* v) { + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + // Merge the set of added files with the set of pre-existing files. + // Drop any deleted files. Store the result in *v. + const std::vector<FileMetaData*>& base_files = base_->files_[level]; + std::vector<FileMetaData*>::const_iterator base_iter = base_files.begin(); + std::vector<FileMetaData*>::const_iterator base_end = base_files.end(); + const FileSet* added = levels_[level].added_files; + v->files_[level].reserve(base_files.size() + added->size()); + for (FileSet::const_iterator added_iter = added->begin(); + added_iter != added->end(); + ++added_iter) { + // Add all smaller files listed in base_ + for (std::vector<FileMetaData*>::const_iterator bpos + = std::upper_bound(base_iter, base_end, *added_iter, cmp); + base_iter != bpos; + ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + + MaybeAddFile(v, level, *added_iter); + } + + // Add remaining base files + for (; base_iter != base_end; ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + +#ifndef NDEBUG + // Make sure there is no overlap in levels > 0 + if (level > 0) { + for (uint32_t i = 1; i < v->files_[level].size(); i++) { + const InternalKey& prev_end = v->files_[level][i-1]->largest; + const InternalKey& this_begin = v->files_[level][i]->smallest; + if (vset_->icmp_.Compare(prev_end, this_begin) >= 0) { + fprintf(stderr, "overlapping ranges in same level %s vs. %s\n", + prev_end.DebugString().c_str(), + this_begin.DebugString().c_str()); + abort(); + } + } + } +#endif + } + } + + void MaybeAddFile(Version* v, int level, FileMetaData* f) { + if (levels_[level].deleted_files.count(f->number) > 0) { + // File is deleted: do nothing + } else { + std::vector<FileMetaData*>* files = &v->files_[level]; + if (level > 0 && !files->empty()) { + // Must not overlap + assert(vset_->icmp_.Compare((*files)[files->size()-1]->largest, + f->smallest) < 0); + } + f->refs++; + files->push_back(f); + } + } +}; + +VersionSet::VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator* cmp) + : env_(options->env), + dbname_(dbname), + options_(options), + table_cache_(table_cache), + icmp_(*cmp), + next_file_number_(2), + manifest_file_number_(0), // Filled by Recover() + last_sequence_(0), + log_number_(0), + prev_log_number_(0), + descriptor_file_(NULL), + descriptor_log_(NULL), + dummy_versions_(this), + current_(NULL) { + AppendVersion(new Version(this)); +} + +VersionSet::~VersionSet() { + current_->Unref(); + assert(dummy_versions_.next_ == &dummy_versions_); // List must be empty + delete descriptor_log_; + delete descriptor_file_; +} + +void VersionSet::AppendVersion(Version* v) { + // Make "v" current + assert(v->refs_ == 0); + assert(v != current_); + if (current_ != NULL) { + current_->Unref(); + } + current_ = v; + v->Ref(); + + // Append to linked list + v->prev_ = dummy_versions_.prev_; + v->next_ = &dummy_versions_; + v->prev_->next_ = v; + v->next_->prev_ = v; +} + +Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { + if (edit->has_log_number_) { + assert(edit->log_number_ >= log_number_); + assert(edit->log_number_ < next_file_number_); + } else { + edit->SetLogNumber(log_number_); + } + + if (!edit->has_prev_log_number_) { + edit->SetPrevLogNumber(prev_log_number_); + } + + edit->SetNextFile(next_file_number_); + edit->SetLastSequence(last_sequence_); + + Version* v = new Version(this); + { + Builder builder(this, current_); + builder.Apply(edit); + builder.SaveTo(v); + } + Finalize(v); + + // Initialize new descriptor log file if necessary by creating + // a temporary file that contains a snapshot of the current version. + std::string new_manifest_file; + Status s; + if (descriptor_log_ == NULL) { + // No reason to unlock *mu here since we only hit this path in the + // first call to LogAndApply (when opening the database). + assert(descriptor_file_ == NULL); + new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_); + edit->SetNextFile(next_file_number_); + s = env_->NewWritableFile(new_manifest_file, &descriptor_file_); + if (s.ok()) { + descriptor_log_ = new log::Writer(descriptor_file_); + s = WriteSnapshot(descriptor_log_); + } + } + + // Unlock during expensive MANIFEST log write + { + mu->Unlock(); + + // Write new record to MANIFEST log + if (s.ok()) { + std::string record; + edit->EncodeTo(&record); + s = descriptor_log_->AddRecord(record); + if (s.ok()) { + s = descriptor_file_->Sync(); + } + } + + // If we just created a new descriptor file, install it by writing a + // new CURRENT file that points to it. + if (s.ok() && !new_manifest_file.empty()) { + s = SetCurrentFile(env_, dbname_, manifest_file_number_); + } + + mu->Lock(); + } + + // Install the new version + if (s.ok()) { + AppendVersion(v); + log_number_ = edit->log_number_; + prev_log_number_ = edit->prev_log_number_; + } else { + delete v; + if (!new_manifest_file.empty()) { + delete descriptor_log_; + delete descriptor_file_; + descriptor_log_ = NULL; + descriptor_file_ = NULL; + env_->DeleteFile(new_manifest_file); + } + } + + return s; +} + +Status VersionSet::Recover() { + struct LogReporter : public log::Reader::Reporter { + Status* status; + virtual void Corruption(size_t bytes, const Status& s) { + if (this->status->ok()) *this->status = s; + } + }; + + // Read "CURRENT" file, which contains a pointer to the current manifest file + std::string current; + Status s = ReadFileToString(env_, CurrentFileName(dbname_), ¤t); + if (!s.ok()) { + return s; + } + if (current.empty() || current[current.size()-1] != '\n') { + return Status::Corruption("CURRENT file does not end with newline"); + } + current.resize(current.size() - 1); + + std::string dscname = dbname_ + "/" + current; + SequentialFile* file; + s = env_->NewSequentialFile(dscname, &file); + if (!s.ok()) { + return s; + } + + bool have_log_number = false; + bool have_prev_log_number = false; + bool have_next_file = false; + bool have_last_sequence = false; + uint64_t next_file = 0; + uint64_t last_sequence = 0; + uint64_t log_number = 0; + uint64_t prev_log_number = 0; + Builder builder(this, current_); + + { + LogReporter reporter; + reporter.status = &s; + log::Reader reader(file, &reporter, true/*checksum*/, 0/*initial_offset*/); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch) && s.ok()) { + VersionEdit edit; + s = edit.DecodeFrom(record); + if (s.ok()) { + if (edit.has_comparator_ && + edit.comparator_ != icmp_.user_comparator()->Name()) { + s = Status::InvalidArgument( + edit.comparator_ + "does not match existing comparator ", + icmp_.user_comparator()->Name()); + } + } + + if (s.ok()) { + builder.Apply(&edit); + } + + if (edit.has_log_number_) { + log_number = edit.log_number_; + have_log_number = true; + } + + if (edit.has_prev_log_number_) { + prev_log_number = edit.prev_log_number_; + have_prev_log_number = true; + } + + if (edit.has_next_file_number_) { + next_file = edit.next_file_number_; + have_next_file = true; + } + + if (edit.has_last_sequence_) { + last_sequence = edit.last_sequence_; + have_last_sequence = true; + } + } + } + delete file; + file = NULL; + + if (s.ok()) { + if (!have_next_file) { + s = Status::Corruption("no meta-nextfile entry in descriptor"); + } else if (!have_log_number) { + s = Status::Corruption("no meta-lognumber entry in descriptor"); + } else if (!have_last_sequence) { + s = Status::Corruption("no last-sequence-number entry in descriptor"); + } + + if (!have_prev_log_number) { + prev_log_number = 0; + } + + MarkFileNumberUsed(prev_log_number); + MarkFileNumberUsed(log_number); + } + + if (s.ok()) { + Version* v = new Version(this); + builder.SaveTo(v); + // Install recovered version + Finalize(v); + AppendVersion(v); + manifest_file_number_ = next_file; + next_file_number_ = next_file + 1; + last_sequence_ = last_sequence; + log_number_ = log_number; + prev_log_number_ = prev_log_number; + } + + return s; +} + +void VersionSet::MarkFileNumberUsed(uint64_t number) { + if (next_file_number_ <= number) { + next_file_number_ = number + 1; + } +} + +void VersionSet::Finalize(Version* v) { + // Precomputed best level for next compaction + int best_level = -1; + double best_score = -1; + + for (int level = 0; level < config::kNumLevels-1; level++) { + double score; + if (level == 0) { + // We treat level-0 specially by bounding the number of files + // instead of number of bytes for two reasons: + // + // (1) With larger write-buffer sizes, it is nice not to do too + // many level-0 compactions. + // + // (2) The files in level-0 are merged on every read and + // therefore we wish to avoid too many files when the individual + // file size is small (perhaps because of a small write-buffer + // setting, or very high compression ratios, or lots of + // overwrites/deletions). + score = v->files_[level].size() / + static_cast<double>(config::kL0_CompactionTrigger); + } else { + // Compute the ratio of current size to size limit. + const uint64_t level_bytes = TotalFileSize(v->files_[level]); + score = static_cast<double>(level_bytes) / MaxBytesForLevel(level); + } + + if (score > best_score) { + best_level = level; + best_score = score; + } + } + + v->compaction_level_ = best_level; + v->compaction_score_ = best_score; +} + +Status VersionSet::WriteSnapshot(log::Writer* log) { + // TODO: Break up into multiple records to reduce memory usage on recovery? + + // Save metadata + VersionEdit edit; + edit.SetComparatorName(icmp_.user_comparator()->Name()); + + // Save compaction pointers + for (int level = 0; level < config::kNumLevels; level++) { + if (!compact_pointer_[level].empty()) { + InternalKey key; + key.DecodeFrom(compact_pointer_[level]); + edit.SetCompactPointer(level, key); + } + } + + // Save files + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector<FileMetaData*>& files = current_->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + edit.AddFile(level, f->number, f->file_size, f->smallest, f->largest); + } + } + + std::string record; + edit.EncodeTo(&record); + return log->AddRecord(record); +} + +int VersionSet::NumLevelFiles(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return current_->files_[level].size(); +} + +const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const { + // Update code if kNumLevels changes + assert(config::kNumLevels == 7); + snprintf(scratch->buffer, sizeof(scratch->buffer), + "files[ %d %d %d %d %d %d %d ]", + int(current_->files_[0].size()), + int(current_->files_[1].size()), + int(current_->files_[2].size()), + int(current_->files_[3].size()), + int(current_->files_[4].size()), + int(current_->files_[5].size()), + int(current_->files_[6].size())); + return scratch->buffer; +} + +uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { + uint64_t result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector<FileMetaData*>& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + if (icmp_.Compare(files[i]->largest, ikey) <= 0) { + // Entire file is before "ikey", so just add the file size + result += files[i]->file_size; + } else if (icmp_.Compare(files[i]->smallest, ikey) > 0) { + // Entire file is after "ikey", so ignore + if (level > 0) { + // Files other than level 0 are sorted by meta->smallest, so + // no further files in this level will contain data for + // "ikey". + break; + } + } else { + // "ikey" falls in the range for this table. Add the + // approximate offset of "ikey" within the table. + Table* tableptr; + Iterator* iter = table_cache_->NewIterator( + ReadOptions(), files[i]->number, files[i]->file_size, &tableptr); + if (tableptr != NULL) { + result += tableptr->ApproximateOffsetOf(ikey.Encode()); + } + delete iter; + } + } + } + return result; +} + +void VersionSet::AddLiveFiles(std::set<uint64_t>* live) { + for (Version* v = dummy_versions_.next_; + v != &dummy_versions_; + v = v->next_) { + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector<FileMetaData*>& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + live->insert(files[i]->number); + } + } + } +} + +int64_t VersionSet::NumLevelBytes(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return TotalFileSize(current_->files_[level]); +} + +int64_t VersionSet::MaxNextLevelOverlappingBytes() { + int64_t result = 0; + std::vector<FileMetaData*> overlaps; + for (int level = 1; level < config::kNumLevels - 1; level++) { + for (size_t i = 0; i < current_->files_[level].size(); i++) { + const FileMetaData* f = current_->files_[level][i]; + current_->GetOverlappingInputs(level+1, &f->smallest, &f->largest, + &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > result) { + result = sum; + } + } + } + return result; +} + +// Stores the minimal range that covers all entries in inputs in +// *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange(const std::vector<FileMetaData*>& inputs, + InternalKey* smallest, + InternalKey* largest) { + assert(!inputs.empty()); + smallest->Clear(); + largest->Clear(); + for (size_t i = 0; i < inputs.size(); i++) { + FileMetaData* f = inputs[i]; + if (i == 0) { + *smallest = f->smallest; + *largest = f->largest; + } else { + if (icmp_.Compare(f->smallest, *smallest) < 0) { + *smallest = f->smallest; + } + if (icmp_.Compare(f->largest, *largest) > 0) { + *largest = f->largest; + } + } + } +} + +// Stores the minimal range that covers all entries in inputs1 and inputs2 +// in *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange2(const std::vector<FileMetaData*>& inputs1, + const std::vector<FileMetaData*>& inputs2, + InternalKey* smallest, + InternalKey* largest) { + std::vector<FileMetaData*> all = inputs1; + all.insert(all.end(), inputs2.begin(), inputs2.end()); + GetRange(all, smallest, largest); +} + +Iterator* VersionSet::MakeInputIterator(Compaction* c) { + ReadOptions options; + options.verify_checksums = options_->paranoid_checks; + options.fill_cache = false; + + // Level-0 files have to be merged together. For other levels, + // we will make a concatenating iterator per level. + // TODO(opt): use concatenating iterator for level-0 if there is no overlap + const int space = (c->level() == 0 ? c->inputs_[0].size() + 1 : 2); + Iterator** list = new Iterator*[space]; + int num = 0; + for (int which = 0; which < 2; which++) { + if (!c->inputs_[which].empty()) { + if (c->level() + which == 0) { + const std::vector<FileMetaData*>& files = c->inputs_[which]; + for (size_t i = 0; i < files.size(); i++) { + list[num++] = table_cache_->NewIterator( + options, files[i]->number, files[i]->file_size); + } + } else { + // Create concatenating iterator for the files from this level + list[num++] = NewTwoLevelIterator( + new Version::LevelFileNumIterator(icmp_, &c->inputs_[which]), + &GetFileIterator, table_cache_, options); + } + } + } + assert(num <= space); + Iterator* result = NewMergingIterator(&icmp_, list, num); + delete[] list; + return result; +} + +Compaction* VersionSet::PickCompaction() { + Compaction* c; + int level; + + // We prefer compactions triggered by too much data in a level over + // the compactions triggered by seeks. + const bool size_compaction = (current_->compaction_score_ >= 1); + const bool seek_compaction = (current_->file_to_compact_ != NULL); + if (size_compaction) { + level = current_->compaction_level_; + assert(level >= 0); + assert(level+1 < config::kNumLevels); + c = new Compaction(level); + + // Pick the first file that comes after compact_pointer_[level] + for (size_t i = 0; i < current_->files_[level].size(); i++) { + FileMetaData* f = current_->files_[level][i]; + if (compact_pointer_[level].empty() || + icmp_.Compare(f->largest.Encode(), compact_pointer_[level]) > 0) { + c->inputs_[0].push_back(f); + break; + } + } + if (c->inputs_[0].empty()) { + // Wrap-around to the beginning of the key space + c->inputs_[0].push_back(current_->files_[level][0]); + } + } else if (seek_compaction) { + level = current_->file_to_compact_level_; + c = new Compaction(level); + c->inputs_[0].push_back(current_->file_to_compact_); + } else { + return NULL; + } + + c->input_version_ = current_; + c->input_version_->Ref(); + + // Files in level 0 may overlap each other, so pick up all overlapping ones + if (level == 0) { + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + // Note that the next call will discard the file we placed in + // c->inputs_[0] earlier and replace it with an overlapping set + // which will include the picked file. + current_->GetOverlappingInputs(0, &smallest, &largest, &c->inputs_[0]); + assert(!c->inputs_[0].empty()); + } + + SetupOtherInputs(c); + + return c; +} + +void VersionSet::SetupOtherInputs(Compaction* c) { + const int level = c->level(); + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + + current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]); + + // Get entire range covered by compaction + InternalKey all_start, all_limit; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + + // See if we can grow the number of inputs in "level" without + // changing the number of "level+1" files we pick up. + if (!c->inputs_[1].empty()) { + std::vector<FileMetaData*> expanded0; + current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0); + const int64_t inputs0_size = TotalFileSize(c->inputs_[0]); + const int64_t inputs1_size = TotalFileSize(c->inputs_[1]); + const int64_t expanded0_size = TotalFileSize(expanded0); + if (expanded0.size() > c->inputs_[0].size() && + inputs1_size + expanded0_size < kExpandedCompactionByteSizeLimit) { + InternalKey new_start, new_limit; + GetRange(expanded0, &new_start, &new_limit); + std::vector<FileMetaData*> expanded1; + current_->GetOverlappingInputs(level+1, &new_start, &new_limit, + &expanded1); + if (expanded1.size() == c->inputs_[1].size()) { + Log(options_->info_log, + "Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n", + level, + int(c->inputs_[0].size()), + int(c->inputs_[1].size()), + long(inputs0_size), long(inputs1_size), + int(expanded0.size()), + int(expanded1.size()), + long(expanded0_size), long(inputs1_size)); + smallest = new_start; + largest = new_limit; + c->inputs_[0] = expanded0; + c->inputs_[1] = expanded1; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + } + } + } + + // Compute the set of grandparent files that overlap this compaction + // (parent == level+1; grandparent == level+2) + if (level + 2 < config::kNumLevels) { + current_->GetOverlappingInputs(level + 2, &all_start, &all_limit, + &c->grandparents_); + } + + if (false) { + Log(options_->info_log, "Compacting %d '%s' .. '%s'", + level, + smallest.DebugString().c_str(), + largest.DebugString().c_str()); + } + + // Update the place where we will do the next compaction for this level. + // We update this immediately instead of waiting for the VersionEdit + // to be applied so that if the compaction fails, we will try a different + // key range next time. + compact_pointer_[level] = largest.Encode().ToString(); + c->edit_.SetCompactPointer(level, largest); +} + +Compaction* VersionSet::CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end) { + std::vector<FileMetaData*> inputs; + current_->GetOverlappingInputs(level, begin, end, &inputs); + if (inputs.empty()) { + return NULL; + } + + // Avoid compacting too much in one shot in case the range is large. + const uint64_t limit = MaxFileSizeForLevel(level); + uint64_t total = 0; + for (size_t i = 0; i < inputs.size(); i++) { + uint64_t s = inputs[i]->file_size; + total += s; + if (total >= limit) { + inputs.resize(i + 1); + break; + } + } + + Compaction* c = new Compaction(level); + c->input_version_ = current_; + c->input_version_->Ref(); + c->inputs_[0] = inputs; + SetupOtherInputs(c); + return c; +} + +Compaction::Compaction(int level) + : level_(level), + max_output_file_size_(MaxFileSizeForLevel(level)), + input_version_(NULL), + grandparent_index_(0), + seen_key_(false), + overlapped_bytes_(0) { + for (int i = 0; i < config::kNumLevels; i++) { + level_ptrs_[i] = 0; + } +} + +Compaction::~Compaction() { + if (input_version_ != NULL) { + input_version_->Unref(); + } +} + +bool Compaction::IsTrivialMove() const { + // Avoid a move if there is lots of overlapping grandparent data. + // Otherwise, the move could create a parent file that will require + // a very expensive merge later on. + return (num_input_files(0) == 1 && + num_input_files(1) == 0 && + TotalFileSize(grandparents_) <= kMaxGrandParentOverlapBytes); +} + +void Compaction::AddInputDeletions(VersionEdit* edit) { + for (int which = 0; which < 2; which++) { + for (size_t i = 0; i < inputs_[which].size(); i++) { + edit->DeleteFile(level_ + which, inputs_[which][i]->number); + } + } +} + +bool Compaction::IsBaseLevelForKey(const Slice& user_key) { + // Maybe use binary search to find right entry instead of linear search? + const Comparator* user_cmp = input_version_->vset_->icmp_.user_comparator(); + for (int lvl = level_ + 2; lvl < config::kNumLevels; lvl++) { + const std::vector<FileMetaData*>& files = input_version_->files_[lvl]; + for (; level_ptrs_[lvl] < files.size(); ) { + FileMetaData* f = files[level_ptrs_[lvl]]; + if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) { + // We've advanced far enough + if (user_cmp->Compare(user_key, f->smallest.user_key()) >= 0) { + // Key falls in this file's range, so definitely not base level + return false; + } + break; + } + level_ptrs_[lvl]++; + } + } + return true; +} + +bool Compaction::ShouldStopBefore(const Slice& internal_key) { + // Scan to find earliest grandparent file that contains key. + const InternalKeyComparator* icmp = &input_version_->vset_->icmp_; + while (grandparent_index_ < grandparents_.size() && + icmp->Compare(internal_key, + grandparents_[grandparent_index_]->largest.Encode()) > 0) { + if (seen_key_) { + overlapped_bytes_ += grandparents_[grandparent_index_]->file_size; + } + grandparent_index_++; + } + seen_key_ = true; + + if (overlapped_bytes_ > kMaxGrandParentOverlapBytes) { + // Too much overlap for current output; start new output + overlapped_bytes_ = 0; + return true; + } else { + return false; + } +} + +void Compaction::ReleaseInputs() { + if (input_version_ != NULL) { + input_version_->Unref(); + input_version_ = NULL; + } +} + +} // namespace leveldb diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h new file mode 100644 index 0000000000..61c4c99a08 --- /dev/null +++ b/src/leveldb/db/version_set.h @@ -0,0 +1,379 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// The representation of a DBImpl consists of a set of Versions. The +// newest version is called "current". Older versions may be kept +// around to provide a consistent view to live iterators. +// +// Each Version keeps track of a set of Table files per level. The +// entire set of versions is maintained in a VersionSet. +// +// Version,VersionSet are thread-compatible, but require external +// synchronization on all accesses. + +#ifndef STORAGE_LEVELDB_DB_VERSION_SET_H_ +#define STORAGE_LEVELDB_DB_VERSION_SET_H_ + +#include <map> +#include <set> +#include <vector> +#include "db/dbformat.h" +#include "db/version_edit.h" +#include "port/port.h" + +namespace leveldb { + +namespace log { class Writer; } + +class Compaction; +class Iterator; +class MemTable; +class TableBuilder; +class TableCache; +class Version; +class VersionSet; +class WritableFile; + +// Return the smallest index i such that files[i]->largest >= key. +// Return files.size() if there is no such file. +// REQUIRES: "files" contains a sorted list of non-overlapping files. +extern int FindFile(const InternalKeyComparator& icmp, + const std::vector<FileMetaData*>& files, + const Slice& key); + +// Returns true iff some file in "files" overlaps the user key range +// [*smallest,*largest]. +// smallest==NULL represents a key smaller than all keys in the DB. +// largest==NULL represents a key largest than all keys in the DB. +// REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges +// in sorted order. +extern bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector<FileMetaData*>& files, + const Slice* smallest_user_key, + const Slice* largest_user_key); + +class Version { + public: + // Append to *iters a sequence of iterators that will + // yield the contents of this Version when merged together. + // REQUIRES: This version has been saved (see VersionSet::SaveTo) + void AddIterators(const ReadOptions&, std::vector<Iterator*>* iters); + + // Lookup the value for key. If found, store it in *val and + // return OK. Else return a non-OK status. Fills *stats. + // REQUIRES: lock is not held + struct GetStats { + FileMetaData* seek_file; + int seek_file_level; + }; + Status Get(const ReadOptions&, const LookupKey& key, std::string* val, + GetStats* stats); + + // Adds "stats" into the current state. Returns true if a new + // compaction may need to be triggered, false otherwise. + // REQUIRES: lock is held + bool UpdateStats(const GetStats& stats); + + // Reference count management (so Versions do not disappear out from + // under live iterators) + void Ref(); + void Unref(); + + void GetOverlappingInputs( + int level, + const InternalKey* begin, // NULL means before all keys + const InternalKey* end, // NULL means after all keys + std::vector<FileMetaData*>* inputs); + + // Returns true iff some file in the specified level overlaps + // some part of [*smallest_user_key,*largest_user_key]. + // smallest_user_key==NULL represents a key smaller than all keys in the DB. + // largest_user_key==NULL represents a key largest than all keys in the DB. + bool OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key); + + // Return the level at which we should place a new memtable compaction + // result that covers the range [smallest_user_key,largest_user_key]. + int PickLevelForMemTableOutput(const Slice& smallest_user_key, + const Slice& largest_user_key); + + int NumFiles(int level) const { return files_[level].size(); } + + // Return a human readable string that describes this version's contents. + std::string DebugString() const; + + private: + friend class Compaction; + friend class VersionSet; + + class LevelFileNumIterator; + Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; + + VersionSet* vset_; // VersionSet to which this Version belongs + Version* next_; // Next version in linked list + Version* prev_; // Previous version in linked list + int refs_; // Number of live refs to this version + + // List of files per level + std::vector<FileMetaData*> files_[config::kNumLevels]; + + // Next file to compact based on seek stats. + FileMetaData* file_to_compact_; + int file_to_compact_level_; + + // Level that should be compacted next and its compaction score. + // Score < 1 means compaction is not strictly needed. These fields + // are initialized by Finalize(). + double compaction_score_; + int compaction_level_; + + explicit Version(VersionSet* vset) + : vset_(vset), next_(this), prev_(this), refs_(0), + file_to_compact_(NULL), + file_to_compact_level_(-1), + compaction_score_(-1), + compaction_level_(-1) { + } + + ~Version(); + + // No copying allowed + Version(const Version&); + void operator=(const Version&); +}; + +class VersionSet { + public: + VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator*); + ~VersionSet(); + + // Apply *edit to the current version to form a new descriptor that + // is both saved to persistent state and installed as the new + // current version. Will release *mu while actually writing to the file. + // REQUIRES: *mu is held on entry. + // REQUIRES: no other thread concurrently calls LogAndApply() + Status LogAndApply(VersionEdit* edit, port::Mutex* mu); + + // Recover the last saved descriptor from persistent storage. + Status Recover(); + + // Return the current version. + Version* current() const { return current_; } + + // Return the current manifest file number + uint64_t ManifestFileNumber() const { return manifest_file_number_; } + + // Allocate and return a new file number + uint64_t NewFileNumber() { return next_file_number_++; } + + // Arrange to reuse "file_number" unless a newer file number has + // already been allocated. + // REQUIRES: "file_number" was returned by a call to NewFileNumber(). + void ReuseFileNumber(uint64_t file_number) { + if (next_file_number_ == file_number + 1) { + next_file_number_ = file_number; + } + } + + // Return the number of Table files at the specified level. + int NumLevelFiles(int level) const; + + // Return the combined file size of all files at the specified level. + int64_t NumLevelBytes(int level) const; + + // Return the last sequence number. + uint64_t LastSequence() const { return last_sequence_; } + + // Set the last sequence number to s. + void SetLastSequence(uint64_t s) { + assert(s >= last_sequence_); + last_sequence_ = s; + } + + // Mark the specified file number as used. + void MarkFileNumberUsed(uint64_t number); + + // Return the current log file number. + uint64_t LogNumber() const { return log_number_; } + + // Return the log file number for the log file that is currently + // being compacted, or zero if there is no such log file. + uint64_t PrevLogNumber() const { return prev_log_number_; } + + // Pick level and inputs for a new compaction. + // Returns NULL if there is no compaction to be done. + // Otherwise returns a pointer to a heap-allocated object that + // describes the compaction. Caller should delete the result. + Compaction* PickCompaction(); + + // Return a compaction object for compacting the range [begin,end] in + // the specified level. Returns NULL if there is nothing in that + // level that overlaps the specified range. Caller should delete + // the result. + Compaction* CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t MaxNextLevelOverlappingBytes(); + + // Create an iterator that reads over the compaction inputs for "*c". + // The caller should delete the iterator when no longer needed. + Iterator* MakeInputIterator(Compaction* c); + + // Returns true iff some level needs a compaction. + bool NeedsCompaction() const { + Version* v = current_; + return (v->compaction_score_ >= 1) || (v->file_to_compact_ != NULL); + } + + // Add all files listed in any live version to *live. + // May also mutate some internal state. + void AddLiveFiles(std::set<uint64_t>* live); + + // Return the approximate offset in the database of the data for + // "key" as of version "v". + uint64_t ApproximateOffsetOf(Version* v, const InternalKey& key); + + // Return a human-readable short (single-line) summary of the number + // of files per level. Uses *scratch as backing store. + struct LevelSummaryStorage { + char buffer[100]; + }; + const char* LevelSummary(LevelSummaryStorage* scratch) const; + + private: + class Builder; + + friend class Compaction; + friend class Version; + + void Finalize(Version* v); + + void GetRange(const std::vector<FileMetaData*>& inputs, + InternalKey* smallest, + InternalKey* largest); + + void GetRange2(const std::vector<FileMetaData*>& inputs1, + const std::vector<FileMetaData*>& inputs2, + InternalKey* smallest, + InternalKey* largest); + + void SetupOtherInputs(Compaction* c); + + // Save current contents to *log + Status WriteSnapshot(log::Writer* log); + + void AppendVersion(Version* v); + + Env* const env_; + const std::string dbname_; + const Options* const options_; + TableCache* const table_cache_; + const InternalKeyComparator icmp_; + uint64_t next_file_number_; + uint64_t manifest_file_number_; + uint64_t last_sequence_; + uint64_t log_number_; + uint64_t prev_log_number_; // 0 or backing store for memtable being compacted + + // Opened lazily + WritableFile* descriptor_file_; + log::Writer* descriptor_log_; + Version dummy_versions_; // Head of circular doubly-linked list of versions. + Version* current_; // == dummy_versions_.prev_ + + // Per-level key at which the next compaction at that level should start. + // Either an empty string, or a valid InternalKey. + std::string compact_pointer_[config::kNumLevels]; + + // No copying allowed + VersionSet(const VersionSet&); + void operator=(const VersionSet&); +}; + +// A Compaction encapsulates information about a compaction. +class Compaction { + public: + ~Compaction(); + + // Return the level that is being compacted. Inputs from "level" + // and "level+1" will be merged to produce a set of "level+1" files. + int level() const { return level_; } + + // Return the object that holds the edits to the descriptor done + // by this compaction. + VersionEdit* edit() { return &edit_; } + + // "which" must be either 0 or 1 + int num_input_files(int which) const { return inputs_[which].size(); } + + // Return the ith input file at "level()+which" ("which" must be 0 or 1). + FileMetaData* input(int which, int i) const { return inputs_[which][i]; } + + // Maximum size of files to build during this compaction. + uint64_t MaxOutputFileSize() const { return max_output_file_size_; } + + // Is this a trivial compaction that can be implemented by just + // moving a single input file to the next level (no merging or splitting) + bool IsTrivialMove() const; + + // Add all inputs to this compaction as delete operations to *edit. + void AddInputDeletions(VersionEdit* edit); + + // Returns true if the information we have available guarantees that + // the compaction is producing data in "level+1" for which no data exists + // in levels greater than "level+1". + bool IsBaseLevelForKey(const Slice& user_key); + + // Returns true iff we should stop building the current output + // before processing "internal_key". + bool ShouldStopBefore(const Slice& internal_key); + + // Release the input version for the compaction, once the compaction + // is successful. + void ReleaseInputs(); + + private: + friend class Version; + friend class VersionSet; + + explicit Compaction(int level); + + int level_; + uint64_t max_output_file_size_; + Version* input_version_; + VersionEdit edit_; + + // Each compaction reads inputs from "level_" and "level_+1" + std::vector<FileMetaData*> inputs_[2]; // The two sets of inputs + + // State used to check for number of of overlapping grandparent files + // (parent == level_ + 1, grandparent == level_ + 2) + std::vector<FileMetaData*> grandparents_; + size_t grandparent_index_; // Index in grandparent_starts_ + bool seen_key_; // Some output key has been seen + int64_t overlapped_bytes_; // Bytes of overlap between current output + // and grandparent files + + // State for implementing IsBaseLevelForKey + + // level_ptrs_ holds indices into input_version_->levels_: our state + // is that we are positioned at one of the file ranges for each + // higher level than the ones involved in this compaction (i.e. for + // all L >= level_ + 2). + size_t level_ptrs_[config::kNumLevels]; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_SET_H_ diff --git a/src/leveldb/db/version_set_test.cc b/src/leveldb/db/version_set_test.cc new file mode 100644 index 0000000000..501e34d133 --- /dev/null +++ b/src/leveldb/db/version_set_test.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class FindFileTest { + public: + std::vector<FileMetaData*> files_; + bool disjoint_sorted_files_; + + FindFileTest() : disjoint_sorted_files_(true) { } + + ~FindFileTest() { + for (int i = 0; i < files_.size(); i++) { + delete files_[i]; + } + } + + void Add(const char* smallest, const char* largest, + SequenceNumber smallest_seq = 100, + SequenceNumber largest_seq = 100) { + FileMetaData* f = new FileMetaData; + f->number = files_.size() + 1; + f->smallest = InternalKey(smallest, smallest_seq, kTypeValue); + f->largest = InternalKey(largest, largest_seq, kTypeValue); + files_.push_back(f); + } + + int Find(const char* key) { + InternalKey target(key, 100, kTypeValue); + InternalKeyComparator cmp(BytewiseComparator()); + return FindFile(cmp, files_, target.Encode()); + } + + bool Overlaps(const char* smallest, const char* largest) { + InternalKeyComparator cmp(BytewiseComparator()); + Slice s(smallest != NULL ? smallest : ""); + Slice l(largest != NULL ? largest : ""); + return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, files_, + (smallest != NULL ? &s : NULL), + (largest != NULL ? &l : NULL)); + } +}; + +TEST(FindFileTest, Empty) { + ASSERT_EQ(0, Find("foo")); + ASSERT_TRUE(! Overlaps("a", "z")); + ASSERT_TRUE(! Overlaps(NULL, "z")); + ASSERT_TRUE(! Overlaps("a", NULL)); + ASSERT_TRUE(! Overlaps(NULL, NULL)); +} + +TEST(FindFileTest, Single) { + Add("p", "q"); + ASSERT_EQ(0, Find("a")); + ASSERT_EQ(0, Find("p")); + ASSERT_EQ(0, Find("p1")); + ASSERT_EQ(0, Find("q")); + ASSERT_EQ(1, Find("q1")); + ASSERT_EQ(1, Find("z")); + + ASSERT_TRUE(! Overlaps("a", "b")); + ASSERT_TRUE(! Overlaps("z1", "z2")); + ASSERT_TRUE(Overlaps("a", "p")); + ASSERT_TRUE(Overlaps("a", "q")); + ASSERT_TRUE(Overlaps("a", "z")); + ASSERT_TRUE(Overlaps("p", "p1")); + ASSERT_TRUE(Overlaps("p", "q")); + ASSERT_TRUE(Overlaps("p", "z")); + ASSERT_TRUE(Overlaps("p1", "p2")); + ASSERT_TRUE(Overlaps("p1", "z")); + ASSERT_TRUE(Overlaps("q", "q")); + ASSERT_TRUE(Overlaps("q", "q1")); + + ASSERT_TRUE(! Overlaps(NULL, "j")); + ASSERT_TRUE(! Overlaps("r", NULL)); + ASSERT_TRUE(Overlaps(NULL, "p")); + ASSERT_TRUE(Overlaps(NULL, "p1")); + ASSERT_TRUE(Overlaps("q", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); +} + + +TEST(FindFileTest, Multiple) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_EQ(0, Find("100")); + ASSERT_EQ(0, Find("150")); + ASSERT_EQ(0, Find("151")); + ASSERT_EQ(0, Find("199")); + ASSERT_EQ(0, Find("200")); + ASSERT_EQ(1, Find("201")); + ASSERT_EQ(1, Find("249")); + ASSERT_EQ(1, Find("250")); + ASSERT_EQ(2, Find("251")); + ASSERT_EQ(2, Find("299")); + ASSERT_EQ(2, Find("300")); + ASSERT_EQ(2, Find("349")); + ASSERT_EQ(2, Find("350")); + ASSERT_EQ(3, Find("351")); + ASSERT_EQ(3, Find("400")); + ASSERT_EQ(3, Find("450")); + ASSERT_EQ(4, Find("451")); + + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("251", "299")); + ASSERT_TRUE(! Overlaps("451", "500")); + ASSERT_TRUE(! Overlaps("351", "399")); + + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); +} + +TEST(FindFileTest, MultipleNullBoundaries) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_TRUE(! Overlaps(NULL, "149")); + ASSERT_TRUE(! Overlaps("451", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); + ASSERT_TRUE(Overlaps(NULL, "150")); + ASSERT_TRUE(Overlaps(NULL, "199")); + ASSERT_TRUE(Overlaps(NULL, "200")); + ASSERT_TRUE(Overlaps(NULL, "201")); + ASSERT_TRUE(Overlaps(NULL, "400")); + ASSERT_TRUE(Overlaps(NULL, "800")); + ASSERT_TRUE(Overlaps("100", NULL)); + ASSERT_TRUE(Overlaps("200", NULL)); + ASSERT_TRUE(Overlaps("449", NULL)); + ASSERT_TRUE(Overlaps("450", NULL)); +} + +TEST(FindFileTest, OverlapSequenceChecks) { + Add("200", "200", 5000, 3000); + ASSERT_TRUE(! Overlaps("199", "199")); + ASSERT_TRUE(! Overlaps("201", "300")); + ASSERT_TRUE(Overlaps("200", "200")); + ASSERT_TRUE(Overlaps("190", "200")); + ASSERT_TRUE(Overlaps("200", "210")); +} + +TEST(FindFileTest, OverlappingFiles) { + Add("150", "600"); + Add("400", "500"); + disjoint_sorted_files_ = false; + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("601", "700")); + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); + ASSERT_TRUE(Overlaps("450", "700")); + ASSERT_TRUE(Overlaps("600", "700")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/write_batch.cc b/src/leveldb/db/write_batch.cc new file mode 100644 index 0000000000..33f4a4257e --- /dev/null +++ b/src/leveldb/db/write_batch.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch::rep_ := +// sequence: fixed64 +// count: fixed32 +// data: record[count] +// record := +// kTypeValue varstring varstring | +// kTypeDeletion varstring +// varstring := +// len: varint32 +// data: uint8[len] + +#include "leveldb/write_batch.h" + +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "util/coding.h" + +namespace leveldb { + +// WriteBatch header has an 8-byte sequence number followed by a 4-byte count. +static const size_t kHeader = 12; + +WriteBatch::WriteBatch() { + Clear(); +} + +WriteBatch::~WriteBatch() { } + +WriteBatch::Handler::~Handler() { } + +void WriteBatch::Clear() { + rep_.clear(); + rep_.resize(kHeader); +} + +Status WriteBatch::Iterate(Handler* handler) const { + Slice input(rep_); + if (input.size() < kHeader) { + return Status::Corruption("malformed WriteBatch (too small)"); + } + + input.remove_prefix(kHeader); + Slice key, value; + int found = 0; + while (!input.empty()) { + found++; + char tag = input[0]; + input.remove_prefix(1); + switch (tag) { + case kTypeValue: + if (GetLengthPrefixedSlice(&input, &key) && + GetLengthPrefixedSlice(&input, &value)) { + handler->Put(key, value); + } else { + return Status::Corruption("bad WriteBatch Put"); + } + break; + case kTypeDeletion: + if (GetLengthPrefixedSlice(&input, &key)) { + handler->Delete(key); + } else { + return Status::Corruption("bad WriteBatch Delete"); + } + break; + default: + return Status::Corruption("unknown WriteBatch tag"); + } + } + if (found != WriteBatchInternal::Count(this)) { + return Status::Corruption("WriteBatch has wrong count"); + } else { + return Status::OK(); + } +} + +int WriteBatchInternal::Count(const WriteBatch* b) { + return DecodeFixed32(b->rep_.data() + 8); +} + +void WriteBatchInternal::SetCount(WriteBatch* b, int n) { + EncodeFixed32(&b->rep_[8], n); +} + +SequenceNumber WriteBatchInternal::Sequence(const WriteBatch* b) { + return SequenceNumber(DecodeFixed64(b->rep_.data())); +} + +void WriteBatchInternal::SetSequence(WriteBatch* b, SequenceNumber seq) { + EncodeFixed64(&b->rep_[0], seq); +} + +void WriteBatch::Put(const Slice& key, const Slice& value) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast<char>(kTypeValue)); + PutLengthPrefixedSlice(&rep_, key); + PutLengthPrefixedSlice(&rep_, value); +} + +void WriteBatch::Delete(const Slice& key) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast<char>(kTypeDeletion)); + PutLengthPrefixedSlice(&rep_, key); +} + +namespace { +class MemTableInserter : public WriteBatch::Handler { + public: + SequenceNumber sequence_; + MemTable* mem_; + + virtual void Put(const Slice& key, const Slice& value) { + mem_->Add(sequence_, kTypeValue, key, value); + sequence_++; + } + virtual void Delete(const Slice& key) { + mem_->Add(sequence_, kTypeDeletion, key, Slice()); + sequence_++; + } +}; +} // namespace + +Status WriteBatchInternal::InsertInto(const WriteBatch* b, + MemTable* memtable) { + MemTableInserter inserter; + inserter.sequence_ = WriteBatchInternal::Sequence(b); + inserter.mem_ = memtable; + return b->Iterate(&inserter); +} + +void WriteBatchInternal::SetContents(WriteBatch* b, const Slice& contents) { + assert(contents.size() >= kHeader); + b->rep_.assign(contents.data(), contents.size()); +} + +void WriteBatchInternal::Append(WriteBatch* dst, const WriteBatch* src) { + SetCount(dst, Count(dst) + Count(src)); + assert(src->rep_.size() >= kHeader); + dst->rep_.append(src->rep_.data() + kHeader, src->rep_.size() - kHeader); +} + +} // namespace leveldb diff --git a/src/leveldb/db/write_batch_internal.h b/src/leveldb/db/write_batch_internal.h new file mode 100644 index 0000000000..4423a7f318 --- /dev/null +++ b/src/leveldb/db/write_batch_internal.h @@ -0,0 +1,49 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ +#define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ + +#include "leveldb/write_batch.h" + +namespace leveldb { + +class MemTable; + +// WriteBatchInternal provides static methods for manipulating a +// WriteBatch that we don't want in the public WriteBatch interface. +class WriteBatchInternal { + public: + // Return the number of entries in the batch. + static int Count(const WriteBatch* batch); + + // Set the count for the number of entries in the batch. + static void SetCount(WriteBatch* batch, int n); + + // Return the seqeunce number for the start of this batch. + static SequenceNumber Sequence(const WriteBatch* batch); + + // Store the specified number as the seqeunce number for the start of + // this batch. + static void SetSequence(WriteBatch* batch, SequenceNumber seq); + + static Slice Contents(const WriteBatch* batch) { + return Slice(batch->rep_); + } + + static size_t ByteSize(const WriteBatch* batch) { + return batch->rep_.size(); + } + + static void SetContents(WriteBatch* batch, const Slice& contents); + + static Status InsertInto(const WriteBatch* batch, MemTable* memtable); + + static void Append(WriteBatch* dst, const WriteBatch* src); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ diff --git a/src/leveldb/db/write_batch_test.cc b/src/leveldb/db/write_batch_test.cc new file mode 100644 index 0000000000..9064e3d85e --- /dev/null +++ b/src/leveldb/db/write_batch_test.cc @@ -0,0 +1,120 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string PrintContents(WriteBatch* b) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* mem = new MemTable(cmp); + mem->Ref(); + std::string state; + Status s = WriteBatchInternal::InsertInto(b, mem); + int count = 0; + Iterator* iter = mem->NewIterator(); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey ikey; + ASSERT_TRUE(ParseInternalKey(iter->key(), &ikey)); + switch (ikey.type) { + case kTypeValue: + state.append("Put("); + state.append(ikey.user_key.ToString()); + state.append(", "); + state.append(iter->value().ToString()); + state.append(")"); + count++; + break; + case kTypeDeletion: + state.append("Delete("); + state.append(ikey.user_key.ToString()); + state.append(")"); + count++; + break; + } + state.append("@"); + state.append(NumberToString(ikey.sequence)); + } + delete iter; + if (!s.ok()) { + state.append("ParseError()"); + } else if (count != WriteBatchInternal::Count(b)) { + state.append("CountMismatch()"); + } + mem->Unref(); + return state; +} + +class WriteBatchTest { }; + +TEST(WriteBatchTest, Empty) { + WriteBatch batch; + ASSERT_EQ("", PrintContents(&batch)); + ASSERT_EQ(0, WriteBatchInternal::Count(&batch)); +} + +TEST(WriteBatchTest, Multiple) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + batch.Put(Slice("baz"), Slice("boo")); + WriteBatchInternal::SetSequence(&batch, 100); + ASSERT_EQ(100, WriteBatchInternal::Sequence(&batch)); + ASSERT_EQ(3, WriteBatchInternal::Count(&batch)); + ASSERT_EQ("Put(baz, boo)@102" + "Delete(box)@101" + "Put(foo, bar)@100", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Corruption) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + WriteBatchInternal::SetSequence(&batch, 200); + Slice contents = WriteBatchInternal::Contents(&batch); + WriteBatchInternal::SetContents(&batch, + Slice(contents.data(),contents.size()-1)); + ASSERT_EQ("Put(foo, bar)@200" + "ParseError()", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Append) { + WriteBatch b1, b2; + WriteBatchInternal::SetSequence(&b1, 200); + WriteBatchInternal::SetSequence(&b2, 300); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("", + PrintContents(&b1)); + b2.Put("a", "va"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200", + PrintContents(&b1)); + b2.Clear(); + b2.Put("b", "vb"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@201", + PrintContents(&b1)); + b2.Delete("foo"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@202" + "Put(b, vb)@201" + "Delete(foo)@203", + PrintContents(&b1)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/doc/bench/db_bench_sqlite3.cc b/src/leveldb/doc/bench/db_bench_sqlite3.cc new file mode 100644 index 0000000000..256793a9db --- /dev/null +++ b/src/leveldb/doc/bench/db_bench_sqlite3.cc @@ -0,0 +1,718 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <stdio.h> +#include <stdlib.h> +#include <sqlite3.h> +#include "util/histogram.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// +// fillseq -- write N values in sequential key order in async mode +// fillseqsync -- write N/100 values in sequential key order in sync mode +// fillseqbatch -- batch write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// fillrandsync -- write N/100 values in random key order in sync mode +// fillrandbatch -- batch write N values in sequential key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillrand100K -- write N/1000 100K values in random order in async mode +// fillseq100K -- write N/1000 100K values in sequential order in async mode +// readseq -- read N times sequentially +// readrandom -- read N times in random order +// readrand100K -- read N/1000 100K values in sequential order in async mode +static const char* FLAGS_benchmarks = + "fillseq," + "fillseqsync," + "fillseqbatch," + "fillrandom," + "fillrandsync," + "fillrandbatch," + "overwrite," + "overwritebatch," + "readrandom," + "readseq," + "fillrand100K," + "fillseq100K," + "readseq," + "readrand100K," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Page size. Default 1 KB. +static int FLAGS_page_size = 1024; + +// Number of pages. +// Default cache size = FLAGS_page_size * FLAGS_num_pages = 4 MB. +static int FLAGS_num_pages = 4096; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// If true, we allow batch writes to occur +static bool FLAGS_transaction = true; + +// If true, we enable Write-Ahead Logging +static bool FLAGS_WAL_enabled = true; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +inline +static void ExecErrorCheck(int status, char *err_msg) { + if (status != SQLITE_OK) { + fprintf(stderr, "SQL error: %s\n", err_msg); + sqlite3_free(err_msg); + exit(1); + } +} + +inline +static void StepErrorCheck(int status) { + if (status != SQLITE_DONE) { + fprintf(stderr, "SQL step error: status = %d\n", status); + exit(1); + } +} + +inline +static void ErrorCheck(int status) { + if (status != SQLITE_OK) { + fprintf(stderr, "sqlite3 error: status = %d\n", status); + exit(1); + } +} + +inline +static void WalCheckpoint(sqlite3* db_) { + // Flush all writes to disk + if (FLAGS_WAL_enabled) { + sqlite3_wal_checkpoint_v2(db_, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL); + } +} + +namespace leveldb { + +// Helper for quickly generating random data. +namespace { +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +} // namespace + +class Benchmark { + private: + sqlite3* db_; + int db_num_; + int num_; + int reads_; + double start_; + double last_op_finish_; + int64_t bytes_; + std::string message_; + Histogram hist_; + RandomGenerator gen_; + Random rand_; + + // State kept for progress messages + int done_; + int next_report_; // When to report next + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each\n", FLAGS_value_size); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + } + + void PrintEnvironment() { + fprintf(stderr, "SQLite: version %s\n", SQLITE_VERSION); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + void Start() { + start_ = Env::Default()->NowMicros() * 1e-6; + bytes_ = 0; + message_.clear(); + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + next_report_ = 100; + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros() * 1e-6; + double micros = (now - last_op_finish_) * 1e6; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void Stop(const Slice& name) { + double finish = Env::Default()->NowMicros() * 1e-6; + + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + if (bytes_ > 0) { + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / (finish - start_)); + if (!message_.empty()) { + message_ = std::string(rate) + " " + message_; + } else { + message_ = rate; + } + } + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + (finish - start_) * 1e6 / done_, + (message_.empty() ? "" : " "), + message_.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } + + public: + enum Order { + SEQUENTIAL, + RANDOM + }; + enum DBState { + FRESH, + EXISTING + }; + + Benchmark() + : db_(NULL), + db_num_(0), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { + std::vector<std::string> files; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + Env::Default()->GetChildren(test_dir, &files); + if (!FLAGS_use_existing_db) { + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("dbbench_sqlite3")) { + std::string file_name(test_dir); + file_name += "/"; + file_name += files[i]; + Env::Default()->DeleteFile(file_name.c_str()); + } + } + } + } + + ~Benchmark() { + int status = sqlite3_close(db_); + ErrorCheck(status); + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + bytes_ = 0; + Start(); + + bool known = true; + bool write_sync = false; + if (name == Slice("fillseq")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseqbatch")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("fillrandom")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillrandbatch")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("overwrite")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("overwritebatch")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("fillrandsync")) { + write_sync = true; + Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseqsync")) { + write_sync = true; + Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillrand100K")) { + Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseq100K")) { + Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1); + WalCheckpoint(db_); + } else if (name == Slice("readseq")) { + ReadSequential(); + } else if (name == Slice("readrandom")) { + Read(RANDOM, 1); + } else if (name == Slice("readrand100K")) { + int n = reads_; + reads_ /= 1000; + Read(RANDOM, 1); + reads_ = n; + } else { + known = false; + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + if (known) { + Stop(name); + } + } + } + + void Open() { + assert(db_ == NULL); + + int status; + char file_name[100]; + char* err_msg = NULL; + db_num_++; + + // Open database + std::string tmp_dir; + Env::Default()->GetTestDirectory(&tmp_dir); + snprintf(file_name, sizeof(file_name), + "%s/dbbench_sqlite3-%d.db", + tmp_dir.c_str(), + db_num_); + status = sqlite3_open(file_name, &db_); + if (status) { + fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_)); + exit(1); + } + + // Change SQLite cache size + char cache_size[100]; + snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d", + FLAGS_num_pages); + status = sqlite3_exec(db_, cache_size, NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + + // FLAGS_page_size is defaulted to 1024 + if (FLAGS_page_size != 1024) { + char page_size[100]; + snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d", + FLAGS_page_size); + status = sqlite3_exec(db_, page_size, NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + + // Change journal mode to WAL if WAL enabled flag is on + if (FLAGS_WAL_enabled) { + std::string WAL_stmt = "PRAGMA journal_mode = WAL"; + + // LevelDB's default cache size is a combined 4 MB + std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096"; + status = sqlite3_exec(db_, WAL_stmt.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + status = sqlite3_exec(db_, WAL_checkpoint.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + + // Change locking mode to exclusive and create tables/index for database + std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE"; + std::string create_stmt = + "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))"; + std::string stmt_array[] = { locking_stmt, create_stmt }; + int stmt_array_length = sizeof(stmt_array) / sizeof(std::string); + for (int i = 0; i < stmt_array_length; i++) { + status = sqlite3_exec(db_, stmt_array[i].c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + } + + void Write(bool write_sync, Order order, DBState state, + int num_entries, int value_size, int entries_per_batch) { + // Create new database if state == FRESH + if (state == FRESH) { + if (FLAGS_use_existing_db) { + message_ = "skipping (--use_existing_db is true)"; + return; + } + sqlite3_close(db_); + db_ = NULL; + Open(); + Start(); + } + + if (num_entries != num_) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_entries); + message_ = msg; + } + + char* err_msg = NULL; + int status; + + sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt; + std::string replace_str = "REPLACE INTO test (key, value) VALUES (?, ?)"; + std::string begin_trans_str = "BEGIN TRANSACTION;"; + std::string end_trans_str = "END TRANSACTION;"; + + // Check for synchronous flag in options + std::string sync_stmt = (write_sync) ? "PRAGMA synchronous = FULL" : + "PRAGMA synchronous = OFF"; + status = sqlite3_exec(db_, sync_stmt.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + + // Preparing sqlite3 statements + status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, + &replace_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, + &begin_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, + &end_trans_stmt, NULL); + ErrorCheck(status); + + bool transaction = (entries_per_batch > 1); + for (int i = 0; i < num_entries; i += entries_per_batch) { + // Begin write transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(begin_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(begin_trans_stmt); + ErrorCheck(status); + } + + // Create and execute SQL statements + for (int j = 0; j < entries_per_batch; j++) { + const char* value = gen_.Generate(value_size).data(); + + // Create values for key-value pair + const int k = (order == SEQUENTIAL) ? i + j : + (rand_.Next() % num_entries); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + + // Bind KV values into replace_stmt + status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC); + ErrorCheck(status); + status = sqlite3_bind_blob(replace_stmt, 2, value, + value_size, SQLITE_STATIC); + ErrorCheck(status); + + // Execute replace_stmt + bytes_ += value_size + strlen(key); + status = sqlite3_step(replace_stmt); + StepErrorCheck(status); + + // Reset SQLite statement for another use + status = sqlite3_clear_bindings(replace_stmt); + ErrorCheck(status); + status = sqlite3_reset(replace_stmt); + ErrorCheck(status); + + FinishedSingleOp(); + } + + // End write transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(end_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(end_trans_stmt); + ErrorCheck(status); + } + } + + status = sqlite3_finalize(replace_stmt); + ErrorCheck(status); + status = sqlite3_finalize(begin_trans_stmt); + ErrorCheck(status); + status = sqlite3_finalize(end_trans_stmt); + ErrorCheck(status); + } + + void Read(Order order, int entries_per_batch) { + int status; + sqlite3_stmt *read_stmt, *begin_trans_stmt, *end_trans_stmt; + + std::string read_str = "SELECT * FROM test WHERE key = ?"; + std::string begin_trans_str = "BEGIN TRANSACTION;"; + std::string end_trans_str = "END TRANSACTION;"; + + // Preparing sqlite3 statements + status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, + &begin_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, + &end_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, NULL); + ErrorCheck(status); + + bool transaction = (entries_per_batch > 1); + for (int i = 0; i < reads_; i += entries_per_batch) { + // Begin read transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(begin_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(begin_trans_stmt); + ErrorCheck(status); + } + + // Create and execute SQL statements + for (int j = 0; j < entries_per_batch; j++) { + // Create key value + char key[100]; + int k = (order == SEQUENTIAL) ? i + j : (rand_.Next() % reads_); + snprintf(key, sizeof(key), "%016d", k); + + // Bind key value into read_stmt + status = sqlite3_bind_blob(read_stmt, 1, key, 16, SQLITE_STATIC); + ErrorCheck(status); + + // Execute read statement + while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW); + StepErrorCheck(status); + + // Reset SQLite statement for another use + status = sqlite3_clear_bindings(read_stmt); + ErrorCheck(status); + status = sqlite3_reset(read_stmt); + ErrorCheck(status); + FinishedSingleOp(); + } + + // End read transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(end_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(end_trans_stmt); + ErrorCheck(status); + } + } + + status = sqlite3_finalize(read_stmt); + ErrorCheck(status); + status = sqlite3_finalize(begin_trans_stmt); + ErrorCheck(status); + status = sqlite3_finalize(end_trans_stmt); + ErrorCheck(status); + } + + void ReadSequential() { + int status; + sqlite3_stmt *pStmt; + std::string read_str = "SELECT * FROM test ORDER BY key"; + + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, NULL); + ErrorCheck(status); + for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) { + bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2); + FinishedSingleOp(); + } + + status = sqlite3_finalize(pStmt); + ErrorCheck(status); + } + +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + std::string default_db_path; + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (leveldb::Slice(argv[i]) == leveldb::Slice("--no_transaction")) { + FLAGS_transaction = false; + } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) { + FLAGS_page_size = n; + } else if (sscanf(argv[i], "--num_pages=%d%c", &n, &junk) == 1) { + FLAGS_num_pages = n; + } else if (sscanf(argv[i], "--WAL_enabled=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_WAL_enabled = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db=<path> + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb/doc/bench/db_bench_tree_db.cc b/src/leveldb/doc/bench/db_bench_tree_db.cc new file mode 100644 index 0000000000..ed86f031c2 --- /dev/null +++ b/src/leveldb/doc/bench/db_bench_tree_db.cc @@ -0,0 +1,528 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <stdio.h> +#include <stdlib.h> +#include <kcpolydb.h> +#include "util/histogram.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillseqsync -- write N/100 values in sequential key order in sync mode +// fillrandsync -- write N/100 values in random key order in sync mode +// fillrand100K -- write N/1000 100K values in random order in async mode +// fillseq100K -- write N/1000 100K values in seq order in async mode +// readseq -- read N times sequentially +// readseq100K -- read N/1000 100K values in sequential order in async mode +// readrand100K -- read N/1000 100K values in sequential order in async mode +// readrandom -- read N times in random order +static const char* FLAGS_benchmarks = + "fillseq," + "fillseqsync," + "fillrandsync," + "fillrandom," + "overwrite," + "readrandom," + "readseq," + "fillrand100K," + "fillseq100K," + "readseq100K," + "readrand100K," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Cache size. Default 4 MB +static int FLAGS_cache_size = 4194304; + +// Page size. Default 1 KB +static int FLAGS_page_size = 1024; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// Compression flag. If true, compression is on. If false, compression +// is off. +static bool FLAGS_compression = true; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +inline +static void DBSynchronize(kyotocabinet::TreeDB* db_) +{ + // Synchronize will flush writes to disk + if (!db_->synchronize()) { + fprintf(stderr, "synchronize error: %s\n", db_->error().name()); + } +} + +namespace leveldb { + +// Helper for quickly generating random data. +namespace { +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +} // namespace + +class Benchmark { + private: + kyotocabinet::TreeDB* db_; + int db_num_; + int num_; + int reads_; + double start_; + double last_op_finish_; + int64_t bytes_; + std::string message_; + Histogram hist_; + RandomGenerator gen_; + Random rand_; + kyotocabinet::LZOCompressor<kyotocabinet::LZO::RAW> comp_; + + // State kept for progress messages + int done_; + int next_report_; // When to report next + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + } + + void PrintEnvironment() { + fprintf(stderr, "Kyoto Cabinet: version %s, lib ver %d, lib rev %d\n", + kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + void Start() { + start_ = Env::Default()->NowMicros() * 1e-6; + bytes_ = 0; + message_.clear(); + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + next_report_ = 100; + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros() * 1e-6; + double micros = (now - last_op_finish_) * 1e6; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void Stop(const Slice& name) { + double finish = Env::Default()->NowMicros() * 1e-6; + + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + if (bytes_ > 0) { + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / (finish - start_)); + if (!message_.empty()) { + message_ = std::string(rate) + " " + message_; + } else { + message_ = rate; + } + } + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + (finish - start_) * 1e6 / done_, + (message_.empty() ? "" : " "), + message_.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } + + public: + enum Order { + SEQUENTIAL, + RANDOM + }; + enum DBState { + FRESH, + EXISTING + }; + + Benchmark() + : db_(NULL), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { + std::vector<std::string> files; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + Env::Default()->GetChildren(test_dir.c_str(), &files); + if (!FLAGS_use_existing_db) { + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("dbbench_polyDB")) { + std::string file_name(test_dir); + file_name += "/"; + file_name += files[i]; + Env::Default()->DeleteFile(file_name.c_str()); + } + } + } + } + + ~Benchmark() { + if (!db_->close()) { + fprintf(stderr, "close error: %s\n", db_->error().name()); + } + } + + void Run() { + PrintHeader(); + Open(false); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + Start(); + + bool known = true; + bool write_sync = false; + if (name == Slice("fillseq")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1); + + } else if (name == Slice("fillrandom")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("overwrite")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillrandsync")) { + write_sync = true; + Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillseqsync")) { + write_sync = true; + Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillrand100K")) { + Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1); + DBSynchronize(db_); + } else if (name == Slice("fillseq100K")) { + Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1); + DBSynchronize(db_); + } else if (name == Slice("readseq")) { + ReadSequential(); + } else if (name == Slice("readrandom")) { + ReadRandom(); + } else if (name == Slice("readrand100K")) { + int n = reads_; + reads_ /= 1000; + ReadRandom(); + reads_ = n; + } else if (name == Slice("readseq100K")) { + int n = reads_; + reads_ /= 1000; + ReadSequential(); + reads_ = n; + } else { + known = false; + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + if (known) { + Stop(name); + } + } + } + + private: + void Open(bool sync) { + assert(db_ == NULL); + + // Initialize db_ + db_ = new kyotocabinet::TreeDB(); + char file_name[100]; + db_num_++; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + snprintf(file_name, sizeof(file_name), + "%s/dbbench_polyDB-%d.kct", + test_dir.c_str(), + db_num_); + + // Create tuning options and open the database + int open_options = kyotocabinet::PolyDB::OWRITER | + kyotocabinet::PolyDB::OCREATE; + int tune_options = kyotocabinet::TreeDB::TSMALL | + kyotocabinet::TreeDB::TLINEAR; + if (FLAGS_compression) { + tune_options |= kyotocabinet::TreeDB::TCOMPRESS; + db_->tune_compressor(&comp_); + } + db_->tune_options(tune_options); + db_->tune_page_cache(FLAGS_cache_size); + db_->tune_page(FLAGS_page_size); + db_->tune_map(256LL<<20); + if (sync) { + open_options |= kyotocabinet::PolyDB::OAUTOSYNC; + } + if (!db_->open(file_name, open_options)) { + fprintf(stderr, "open error: %s\n", db_->error().name()); + } + } + + void Write(bool sync, Order order, DBState state, + int num_entries, int value_size, int entries_per_batch) { + // Create new database if state == FRESH + if (state == FRESH) { + if (FLAGS_use_existing_db) { + message_ = "skipping (--use_existing_db is true)"; + return; + } + delete db_; + db_ = NULL; + Open(sync); + Start(); // Do not count time taken to destroy/open + } + + if (num_entries != num_) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_entries); + message_ = msg; + } + + // Write to database + for (int i = 0; i < num_entries; i++) + { + const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + bytes_ += value_size + strlen(key); + std::string cpp_key = key; + if (!db_->set(cpp_key, gen_.Generate(value_size).ToString())) { + fprintf(stderr, "set error: %s\n", db_->error().name()); + } + FinishedSingleOp(); + } + } + + void ReadSequential() { + kyotocabinet::DB::Cursor* cur = db_->cursor(); + cur->jump(); + std::string ckey, cvalue; + while (cur->get(&ckey, &cvalue, true)) { + bytes_ += ckey.size() + cvalue.size(); + FinishedSingleOp(); + } + delete cur; + } + + void ReadRandom() { + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = rand_.Next() % reads_; + snprintf(key, sizeof(key), "%016d", k); + db_->get(key, &value); + FinishedSingleOp(); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + std::string default_db_path; + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) { + FLAGS_page_size = n; + } else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_compression = (n == 1) ? true : false; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db=<path> + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb/doc/benchmark.html b/src/leveldb/doc/benchmark.html new file mode 100644 index 0000000000..c4639772c1 --- /dev/null +++ b/src/leveldb/doc/benchmark.html @@ -0,0 +1,459 @@ +<!DOCTYPE html> +<html> +<head> +<title>LevelDB Benchmarks</title> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +<style> +body { + font-family:Helvetica,sans-serif; + padding:20px; +} + +h2 { + padding-top:30px; +} + +table.bn { + width:800px; + border-collapse:collapse; + border:0; + padding:0; +} + +table.bnbase { + width:650px; +} + +table.bn td { + padding:2px 0; +} + +table.bn td.c1 { + font-weight:bold; + width:150px; +} + +table.bn td.c1 div.e { + float:right; + font-weight:normal; +} + +table.bn td.c2 { + width:150px; + text-align:right; + padding:2px; +} + +table.bn td.c3 { + width:350px; +} + +table.bn td.c4 { + width:150px; + font-size:small; + padding-left:4px; +} + +/* chart bars */ +div.bldb { + background-color:#0255df; +} + +div.bkct { + background-color:#df5555; +} + +div.bsql { + background-color:#aadf55; +} + +.code { + font-family:monospace; + font-size:large; +} + +.todo { + color: red; +} + +</style> +</head> +<body> +<h1>LevelDB Benchmarks</h1> +<p>Google, July 2011</p> +<hr> + +<p>In order to test LevelDB's performance, we benchmark it against other well-established database implementations. We compare LevelDB (revision 39) against <a href="http://www.sqlite.org/">SQLite3</a> (version 3.7.6.3) and <a href="http://fallabs.com/kyotocabinet/spex.html">Kyoto Cabinet's</a> (version 1.2.67) TreeDB (a B+Tree based key-value store). We would like to acknowledge Scott Hess and Mikio Hirabayashi for their suggestions and contributions to the SQLite3 and Kyoto Cabinet benchmarks, respectively.</p> + +<p>Benchmarks were all performed on a six-core Intel(R) Xeon(R) CPU X5650 @ 2.67GHz, with 12288 KB of total L3 cache and 12 GB of DDR3 RAM at 1333 MHz. (Note that LevelDB uses at most two CPUs since the benchmarks are single threaded: one to run the benchmark, and one for background compactions.) We ran the benchmarks on two machines (with identical processors), one with an Ext3 file system and one with an Ext4 file system. The machine with the Ext3 file system has a SATA Hitachi HDS721050CLA362 hard drive. The machine with the Ext4 file system has a SATA Samsung HD502HJ hard drive. Both hard drives spin at 7200 RPM and have hard drive write-caching enabled (using `hdparm -W 1 [device]`). The numbers reported below are the median of three measurements.</p> + +<h4>Benchmark Source Code</h4> +<p>We wrote benchmark tools for SQLite and Kyoto TreeDB based on LevelDB's <span class="code">db_bench</span>. The code for each of the benchmarks resides here:</p> +<ul> + <li> <b>LevelDB:</b> <a href="http://code.google.com/p/leveldb/source/browse/trunk/db/db_bench.cc">db/db_bench.cc</a>.</li> + <li> <b>SQLite:</b> <a href="http://code.google.com/p/leveldb/source/browse/#svn%2Ftrunk%2Fdoc%2Fbench%2Fdb_bench_sqlite3.cc">doc/bench/db_bench_sqlite3.cc</a>.</li> + <li> <b>Kyoto TreeDB:</b> <a href="http://code.google.com/p/leveldb/source/browse/#svn%2Ftrunk%2Fdoc%2Fbench%2Fdb_bench_tree_db.cc">doc/bench/db_bench_tree_db.cc</a>.</li> +</ul> + +<h4>Custom Build Specifications</h4> +<ul> +<li>LevelDB: LevelDB was compiled with the <a href="http://code.google.com/p/google-perftools">tcmalloc</a> library and the <a href="http://code.google.com/p/snappy/">Snappy</a> compression library (revision 33). Assertions were disabled.</li> +<li>TreeDB: TreeDB was compiled using the <a href="http://www.oberhumer.com/opensource/lzo/">LZO</a> compression library (version 2.03). Furthermore, we enabled the TSMALL and TLINEAR options when opening the database in order to reduce the footprint of each record.</li> +<li>SQLite: We tuned SQLite's performance, by setting its locking mode to exclusive. We also enabled SQLite's <a href="http://www.sqlite.org/draft/wal.html">write-ahead logging</a>.</li> +</ul> + +<h2>1. Baseline Performance</h2> +<p>This section gives the baseline performance of all the +databases. Following sections show how performance changes as various +parameters are varied. For the baseline:</p> +<ul> + <li> Each database is allowed 4 MB of cache memory.</li> + <li> Databases are opened in <em>asynchronous</em> write mode. + (LevelDB's sync option, TreeDB's OAUTOSYNC option, and + SQLite3's synchronous options are all turned off). I.e., + every write is pushed to the operating system, but the + benchmark does not wait for the write to reach the disk.</li> + <li> Keys are 16 bytes each.</li> + <li> Value are 100 bytes each (with enough redundancy so that + a simple compressor shrinks them to 50% of their original + size).</li> + <li> Sequential reads/writes traverse the key space in increasing order.</li> + <li> Random reads/writes traverse the key space in random order.</li> +</ul> + +<h3>A. Sequential Reads</h3> +<table class="bn bnbase"> +<tr><td class="c1">LevelDB</td> + <td class="c2">4,030,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">1,010,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:95px"> </div></td> +<tr><td class="c1">SQLite3</td> + <td class="c2">383,000 ops/sec</td> + <td class="c3"><div class="bsql" style="width:33px"> </div></td> +</table> +<h3>B. Random Reads</h3> +<table class="bn bnbase"> +<tr><td class="c1">LevelDB</td> + <td class="c2">129,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:298px"> </div></td> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">151,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:350px"> </div></td> +<tr><td class="c1">SQLite3</td> + <td class="c2">134,000 ops/sec</td> + <td class="c3"><div class="bsql" style="width:310px"> </div></td> +</table> +<h3>C. Sequential Writes</h3> +<table class="bn bnbase"> +<tr><td class="c1">LevelDB</td> + <td class="c2">779,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">342,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:154px"> </div></td> +<tr><td class="c1">SQLite3</td> + <td class="c2">48,600 ops/sec</td> + <td class="c3"><div class="bsql" style="width:22px"> </div></td> +</table> +<h3>D. Random Writes</h3> +<table class="bn bnbase"> +<tr><td class="c1">LevelDB</td> + <td class="c2">164,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">88,500 ops/sec</td> + <td class="c3"><div class="bkct" style="width:188px"> </div></td> +<tr><td class="c1">SQLite3</td> + <td class="c2">9,860 ops/sec</td> + <td class="c3"><div class="bsql" style="width:21px"> </div></td> +</table> + +<p>LevelDB outperforms both SQLite3 and TreeDB in sequential and random write operations and sequential read operations. Kyoto Cabinet has the fastest random read operations.</p> + +<h2>2. Write Performance under Different Configurations</h2> +<h3>A. Large Values </h3> +<p>For this benchmark, we start with an empty database, and write 100,000 byte values (~50% compressible). To keep the benchmark running time reasonable, we stop after writing 1000 values.</p> +<h4>Sequential Writes</h4> +<table class="bn bnbase"> +<tr><td class="c1">LevelDB</td> + <td class="c2">1,100 ops/sec</td> + <td class="c3"><div class="bldb" style="width:234px"> </div></td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">1,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:224px"> </div></td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">1,600 ops/sec</td> + <td class="c3"><div class="bsql" style="width:350px"> </div></td></tr> +</table> +<h4>Random Writes</h4> +<table class="bn bnbase"> +<tr><td class="c1">LevelDB</td> + <td class="c2">480 ops/sec</td> + <td class="c3"><div class="bldb" style="width:105px"> </div></td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">1,100 ops/sec</td> + <td class="c3"><div class="bkct" style="width:240px"> </div></td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">1,600 ops/sec</td> + <td class="c3"><div class="bsql" style="width:350px"> </div></td></tr> +</table> +<p>LevelDB doesn't perform as well with large values of 100,000 bytes each. This is because LevelDB writes keys and values at least twice: first time to the transaction log, and second time (during a compaction) to a sorted file. +With larger values, LevelDB's per-operation efficiency is swamped by the +cost of extra copies of large values.</p> +<h3>B. Batch Writes</h3> +<p>A batch write is a set of writes that are applied atomically to the underlying database. A single batch of N writes may be significantly faster than N individual writes. The following benchmark writes one thousand batches where each batch contains one thousand 100-byte values. TreeDB does not support batch writes and is omitted from this benchmark.</p> +<h4>Sequential Writes</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">840,000 entries/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> + <td class="c4">(1.08x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">124,000 entries/sec</td> + <td class="c3"><div class="bsql" style="width:52px"> </div></td> + <td class="c4">(2.55x baseline)</td></tr> +</table> +<h4>Random Writes</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">221,000 entries/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> + <td class="c4">(1.35x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">22,000 entries/sec</td> + <td class="c3"><div class="bsql" style="width:34px"> </div></td> + <td class="c4">(2.23x baseline)</td></tr> +</table> + +<p>Because of the way LevelDB persistent storage is organized, batches of +random writes are not much slower (only a factor of 4x) than batches +of sequential writes.</p> + +<h3>C. Synchronous Writes</h3> +<p>In the following benchmark, we enable the synchronous writing modes +of all of the databases. Since this change significantly slows down the +benchmark, we stop after 10,000 writes. For synchronous write tests, we've +disabled hard drive write-caching (using `hdparm -W 0 [device]`).</p> +<ul> + <li>For LevelDB, we set WriteOptions.sync = true.</li> + <li>In TreeDB, we enabled TreeDB's OAUTOSYNC option.</li> + <li>For SQLite3, we set "PRAGMA synchronous = FULL".</li> +</ul> +<h4>Sequential Writes</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">100 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> + <td class="c4">(0.003x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">7 ops/sec</td> + <td class="c3"><div class="bkct" style="width:27px"> </div></td> + <td class="c4">(0.0004x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">88 ops/sec</td> + <td class="c3"><div class="bsql" style="width:315px"> </div></td> + <td class="c4">(0.002x baseline)</td></tr> +</table> +<h4>Random Writes</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">100 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> + <td class="c4">(0.015x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">8 ops/sec</td> + <td class="c3"><div class="bkct" style="width:29px"> </div></td> + <td class="c4">(0.001x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">88 ops/sec</td> + <td class="c3"><div class="bsql" style="width:314px"> </div></td> + <td class="c4">(0.009x baseline)</td></tr> +</table> + +<p>Also see the <code>ext4</code> performance numbers below +since synchronous writes behave significantly differently +on <code>ext3</code> and <code>ext4</code>.</p> + +<h3>D. Turning Compression Off</h3> + +<p>In the baseline measurements, LevelDB and TreeDB were using +light-weight compression +(<a href="http://code.google.com/p/snappy/">Snappy</a> for LevelDB, +and <a href="http://www.oberhumer.com/opensource/lzo/">LZO</a> for +TreeDB). SQLite3, by default does not use compression. The +experiments below show what happens when compression is disabled in +all of the databases (the SQLite3 numbers are just a copy of +its baseline measurements):</p> + +<h4>Sequential Writes</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">594,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> + <td class="c4">(0.76x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">485,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:239px"> </div></td> + <td class="c4">(1.42x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">48,600 ops/sec</td> + <td class="c3"><div class="bsql" style="width:29px"> </div></td> + <td class="c4">(1.00x baseline)</td></tr> +</table> +<h4>Random Writes</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">135,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:296px"> </div></td> + <td class="c4">(0.82x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">159,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:350px"> </div></td> + <td class="c4">(1.80x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">9,860 ops/sec</td> + <td class="c3"><div class="bsql" style="width:22px"> </div></td> + <td class="c4">(1.00x baseline)</td></tr> +</table> + +<p>LevelDB's write performance is better with compression than without +since compression decreases the amount of data that has to be written +to disk. Therefore LevelDB users can leave compression enabled in +most scenarios without having worry about a tradeoff between space +usage and performance. TreeDB's performance on the other hand is +better without compression than with compression. Presumably this is +because TreeDB's compression library (LZO) is more expensive than +LevelDB's compression library (Snappy).<p> + +<h3>E. Using More Memory</h3> +<p>We increased the overall cache size for each database to 128 MB. For LevelDB, we partitioned 128 MB into a 120 MB write buffer and 8 MB of cache (up from 2 MB of write buffer and 2 MB of cache). For SQLite3, we kept the page size at 1024 bytes, but increased the number of pages to 131,072 (up from 4096). For TreeDB, we also kept the page size at 1024 bytes, but increased the cache size to 128 MB (up from 4 MB).</p> +<h4>Sequential Writes</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">812,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> + <td class="c4">(1.04x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">321,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:138px"> </div></td> + <td class="c4">(0.94x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">48,500 ops/sec</td> + <td class="c3"><div class="bsql" style="width:21px"> </div></td> + <td class="c4">(1.00x baseline)</td></tr> +</table> +<h4>Random Writes</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">355,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> + <td class="c4">(2.16x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">284,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:280px"> </div></td> + <td class="c4">(3.21x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">9,670 ops/sec</td> + <td class="c3"><div class="bsql" style="width:10px"> </div></td> + <td class="c4">(0.98x baseline)</td></tr> +</table> + +<p>SQLite's performance does not change substantially when compared to +the baseline, but the random write performance for both LevelDB and +TreeDB increases significantly. LevelDB's performance improves +because a larger write buffer reduces the need to merge sorted files +(since it creates a smaller number of larger sorted files). TreeDB's +performance goes up because the entire database is available in memory +for fast in-place updates.</p> + + <h2>3. Read Performance under Different Configurations</h2> +<h3>A. Larger Caches</h3> +<p>We increased the overall memory usage to 128 MB for each database. +For LevelDB, we allocated 8 MB to LevelDB's write buffer and 120 MB +to LevelDB's cache. The other databases don't differentiate between a +write buffer and a cache, so we simply set their cache size to 128 +MB.</p> +<h4>Sequential Reads</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">5,210,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> + <td class="c4">(1.29x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">1,070,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:72px"> </div></td> + <td class="c4">(1.06x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">609,000 ops/sec</td> + <td class="c3"><div class="bsql" style="width:41px"> </div></td> + <td class="c4">(1.59x baseline)</td></tr> +</table> + +<h4>Random Reads</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">190,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:144px"> </div></td> + <td class="c4">(1.47x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">463,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:350px"> </div></td> + <td class="c4">(3.07x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">186,000 ops/sec</td> + <td class="c3"><div class="bsql" style="width:141px"> </div></td> + <td class="c4">(1.39x baseline)</td></tr> +</table> + +<p>As expected, the read performance of all of the databases increases +when the caches are enlarged. In particular, TreeDB seems to make +very effective use of a cache that is large enough to hold the entire +database.</p> + +<h3>B. No Compression Reads </h3> +<p>For this benchmark, we populated a database with 1 million entries consisting of 16 byte keys and 100 byte values. We compiled LevelDB and Kyoto Cabinet without compression support, so results that are read out from the database are already uncompressed. We've listed the SQLite3 baseline read performance as a point of comparison.</p> +<h4>Sequential Reads</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">4,880,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:350px"> </div></td> + <td class="c4">(1.21x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">1,230,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:88px"> </div></td> + <td class="c4">(3.60x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">383,000 ops/sec</td> + <td class="c3"><div class="bsql" style="width:27px"> </div></td> + <td class="c4">(1.00x baseline)</td></tr> +</table> +<h4>Random Reads</h4> +<table class="bn"> +<tr><td class="c1">LevelDB</td> + <td class="c2">149,000 ops/sec</td> + <td class="c3"><div class="bldb" style="width:300px"> </div></td> + <td class="c4">(1.16x baseline)</td></tr> +<tr><td class="c1">Kyoto TreeDB</td> + <td class="c2">175,000 ops/sec</td> + <td class="c3"><div class="bkct" style="width:350px"> </div></td> + <td class="c4">(1.16x baseline)</td></tr> +<tr><td class="c1">SQLite3</td> + <td class="c2">134,000 ops/sec</td> + <td class="c3"><div class="bsql" style="width:268px"> </div></td> + <td class="c4">(1.00x baseline)</td></tr> +</table> + +<p>Performance of both LevelDB and TreeDB improves a small amount when +compression is disabled. Note however that under different workloads, +performance may very well be better with compression if it allows more +of the working set to fit in memory.</p> + +<h2>Note about Ext4 Filesystems</h2> +<p>The preceding numbers are for an ext3 file system. Synchronous writes are much slower under <a href="http://en.wikipedia.org/wiki/Ext4">ext4</a> (LevelDB drops to ~31 writes / second and TreeDB drops to ~5 writes / second; SQLite3's synchronous writes do not noticeably drop) due to ext4's different handling of <span class="code">fsync</span> / <span class="code">msync</span> calls. Even LevelDB's asynchronous write performance drops somewhat since it spreads its storage across multiple files and issues <span class="code">fsync</span> calls when switching to a new file.</p> + +<h2>Acknowledgements</h2> +<p>Jeff Dean and Sanjay Ghemawat wrote LevelDB. Kevin Tseng wrote and compiled these benchmarks. Mikio Hirabayashi, Scott Hess, and Gabor Cselle provided help and advice.</p> +</body> +</html> diff --git a/src/leveldb/doc/doc.css b/src/leveldb/doc/doc.css new file mode 100644 index 0000000000..700c564e43 --- /dev/null +++ b/src/leveldb/doc/doc.css @@ -0,0 +1,89 @@ +body { + margin-left: 0.5in; + margin-right: 0.5in; + background: white; + color: black; +} + +h1 { + margin-left: -0.2in; + font-size: 14pt; +} +h2 { + margin-left: -0in; + font-size: 12pt; +} +h3 { + margin-left: -0in; +} +h4 { + margin-left: -0in; +} +hr { + margin-left: -0in; +} + +/* Definition lists: definition term bold */ +dt { + font-weight: bold; +} + +address { + text-align: center; +} +code,samp,var { + color: blue; +} +kbd { + color: #600000; +} +div.note p { + float: right; + width: 3in; + margin-right: 0%; + padding: 1px; + border: 2px solid #6060a0; + background-color: #fffff0; +} + +ul { + margin-top: -0em; + margin-bottom: -0em; +} + +ol { + margin-top: -0em; + margin-bottom: -0em; +} + +UL.nobullets { + list-style-type: none; + list-style-image: none; + margin-left: -1em; +} + +p { + margin: 1em 0 1em 0; + padding: 0 0 0 0; +} + +pre { + line-height: 1.3em; + padding: 0.4em 0 0.8em 0; + margin: 0 0 0 0; + border: 0 0 0 0; + color: blue; +} + +.datatable { + margin-left: auto; + margin-right: auto; + margin-top: 2em; + margin-bottom: 2em; + border: 1px solid; +} + +.datatable td,th { + padding: 0 0.5em 0 0.5em; + text-align: right; +} diff --git a/src/leveldb/doc/impl.html b/src/leveldb/doc/impl.html new file mode 100644 index 0000000000..e870795d23 --- /dev/null +++ b/src/leveldb/doc/impl.html @@ -0,0 +1,213 @@ +<!DOCTYPE html> +<html> +<head> +<link rel="stylesheet" type="text/css" href="doc.css" /> +<title>Leveldb file layout and compactions</title> +</head> + +<body> + +<h1>Files</h1> + +The implementation of leveldb is similar in spirit to the +representation of a single +<a href="http://labs.google.com/papers/bigtable.html"> +Bigtable tablet (section 5.3)</a>. +However the organization of the files that make up the representation +is somewhat different and is explained below. + +<p> +Each database is represented by a set of files stored in a directory. +There are several different types of files as documented below: +<p> +<h2>Log files</h2> +<p> +A log file (*.log) stores a sequence of recent updates. Each update +is appended to the current log file. When the log file reaches a +pre-determined size (approximately 4MB by default), it is converted +to a sorted table (see below) and a new log file is created for future +updates. +<p> +A copy of the current log file is kept in an in-memory structure (the +<code>memtable</code>). This copy is consulted on every read so that read +operations reflect all logged updates. +<p> +<h2>Sorted tables</h2> +<p> +A sorted table (*.sst) stores a sequence of entries sorted by key. +Each entry is either a value for the key, or a deletion marker for the +key. (Deletion markers are kept around to hide obsolete values +present in older sorted tables). +<p> +The set of sorted tables are organized into a sequence of levels. The +sorted table generated from a log file is placed in a special <code>young</code> +level (also called level-0). When the number of young files exceeds a +certain threshold (currently four), all of the young files are merged +together with all of the overlapping level-1 files to produce a +sequence of new level-1 files (we create a new level-1 file for every +2MB of data.) +<p> +Files in the young level may contain overlapping keys. However files +in other levels have distinct non-overlapping key ranges. Consider +level number L where L >= 1. When the combined size of files in +level-L exceeds (10^L) MB (i.e., 10MB for level-1, 100MB for level-2, +...), one file in level-L, and all of the overlapping files in +level-(L+1) are merged to form a set of new files for level-(L+1). +These merges have the effect of gradually migrating new updates from +the young level to the largest level using only bulk reads and writes +(i.e., minimizing expensive seeks). + +<h2>Manifest</h2> +<p> +A MANIFEST file lists the set of sorted tables that make up each +level, the corresponding key ranges, and other important metadata. +A new MANIFEST file (with a new number embedded in the file name) +is created whenever the database is reopened. The MANIFEST file is +formatted as a log, and changes made to the serving state (as files +are added or removed) are appended to this log. +<p> +<h2>Current</h2> +<p> +CURRENT is a simple text file that contains the name of the latest +MANIFEST file. +<p> +<h2>Info logs</h2> +<p> +Informational messages are printed to files named LOG and LOG.old. +<p> +<h2>Others</h2> +<p> +Other files used for miscellaneous purposes may also be present +(LOCK, *.dbtmp). + +<h1>Level 0</h1> +When the log file grows above a certain size (1MB by default): +<ul> +<li>Create a brand new memtable and log file and direct future updates here +<li>In the background: +<ul> +<li>Write the contents of the previous memtable to an sstable +<li>Discard the memtable +<li>Delete the old log file and the old memtable +<li>Add the new sstable to the young (level-0) level. +</ul> +</ul> + +<h1>Compactions</h1> + +<p> +When the size of level L exceeds its limit, we compact it in a +background thread. The compaction picks a file from level L and all +overlapping files from the next level L+1. Note that if a level-L +file overlaps only part of a level-(L+1) file, the entire file at +level-(L+1) is used as an input to the compaction and will be +discarded after the compaction. Aside: because level-0 is special +(files in it may overlap each other), we treat compactions from +level-0 to level-1 specially: a level-0 compaction may pick more than +one level-0 file in case some of these files overlap each other. + +<p> +A compaction merges the contents of the picked files to produce a +sequence of level-(L+1) files. We switch to producing a new +level-(L+1) file after the current output file has reached the target +file size (2MB). We also switch to a new output file when the key +range of the current output file has grown enough to overlap more then +ten level-(L+2) files. This last rule ensures that a later compaction +of a level-(L+1) file will not pick up too much data from level-(L+2). + +<p> +The old files are discarded and the new files are added to the serving +state. + +<p> +Compactions for a particular level rotate through the key space. In +more detail, for each level L, we remember the ending key of the last +compaction at level L. The next compaction for level L will pick the +first file that starts after this key (wrapping around to the +beginning of the key space if there is no such file). + +<p> +Compactions drop overwritten values. They also drop deletion markers +if there are no higher numbered levels that contain a file whose range +overlaps the current key. + +<h2>Timing</h2> + +Level-0 compactions will read up to four 1MB files from level-0, and +at worst all the level-1 files (10MB). I.e., we will read 14MB and +write 14MB. + +<p> +Other than the special level-0 compactions, we will pick one 2MB file +from level L. In the worst case, this will overlap ~ 12 files from +level L+1 (10 because level-(L+1) is ten times the size of level-L, +and another two at the boundaries since the file ranges at level-L +will usually not be aligned with the file ranges at level-L+1). The +compaction will therefore read 26MB and write 26MB. Assuming a disk +IO rate of 100MB/s (ballpark range for modern drives), the worst +compaction cost will be approximately 0.5 second. + +<p> +If we throttle the background writing to something small, say 10% of +the full 100MB/s speed, a compaction may take up to 5 seconds. If the +user is writing at 10MB/s, we might build up lots of level-0 files +(~50 to hold the 5*10MB). This may signficantly increase the cost of +reads due to the overhead of merging more files together on every +read. + +<p> +Solution 1: To reduce this problem, we might want to increase the log +switching threshold when the number of level-0 files is large. Though +the downside is that the larger this threshold, the more memory we will +need to hold the corresponding memtable. + +<p> +Solution 2: We might want to decrease write rate artificially when the +number of level-0 files goes up. + +<p> +Solution 3: We work on reducing the cost of very wide merges. +Perhaps most of the level-0 files will have their blocks sitting +uncompressed in the cache and we will only need to worry about the +O(N) complexity in the merging iterator. + +<h2>Number of files</h2> + +Instead of always making 2MB files, we could make larger files for +larger levels to reduce the total file count, though at the expense of +more bursty compactions. Alternatively, we could shard the set of +files into multiple directories. + +<p> +An experiment on an <code>ext3</code> filesystem on Feb 04, 2011 shows +the following timings to do 100K file opens in directories with +varying number of files: +<table class="datatable"> +<tr><th>Files in directory</th><th>Microseconds to open a file</th></tr> +<tr><td>1000</td><td>9</td> +<tr><td>10000</td><td>10</td> +<tr><td>100000</td><td>16</td> +</table> +So maybe even the sharding is not necessary on modern filesystems? + +<h1>Recovery</h1> + +<ul> +<li> Read CURRENT to find name of the latest committed MANIFEST +<li> Read the named MANIFEST file +<li> Clean up stale files +<li> We could open all sstables here, but it is probably better to be lazy... +<li> Convert log chunk to a new level-0 sstable +<li> Start directing new writes to a new log file with recovered sequence# +</ul> + +<h1>Garbage collection of files</h1> + +<code>DeleteObsoleteFiles()</code> is called at the end of every +compaction and at the end of recovery. It finds the names of all +files in the database. It deletes all log files that are not the +current log file. It deletes all table files that are not referenced +from some level and are not the output of an active compaction. + +</body> +</html> diff --git a/src/leveldb/doc/index.html b/src/leveldb/doc/index.html new file mode 100644 index 0000000000..521d2baf41 --- /dev/null +++ b/src/leveldb/doc/index.html @@ -0,0 +1,549 @@ +<!DOCTYPE html> +<html> +<head> +<link rel="stylesheet" type="text/css" href="doc.css" /> +<title>Leveldb</title> +</head> + +<body> +<h1>Leveldb</h1> +<address>Jeff Dean, Sanjay Ghemawat</address> +<p> +The <code>leveldb</code> library provides a persistent key value store. Keys and +values are arbitrary byte arrays. The keys are ordered within the key +value store according to a user-specified comparator function. + +<p> +<h1>Opening A Database</h1> +<p> +A <code>leveldb</code> database has a name which corresponds to a file system +directory. All of the contents of database are stored in this +directory. The following example shows how to open a database, +creating it if necessary: +<p> +<pre> + #include <assert> + #include "leveldb/db.h" + + leveldb::DB* db; + leveldb::Options options; + options.create_if_missing = true; + leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); + assert(status.ok()); + ... +</pre> +If you want to raise an error if the database already exists, add +the following line before the <code>leveldb::DB::Open</code> call: +<pre> + options.error_if_exists = true; +</pre> +<h1>Status</h1> +<p> +You may have noticed the <code>leveldb::Status</code> type above. Values of this +type are returned by most functions in <code>leveldb</code> that may encounter an +error. You can check if such a result is ok, and also print an +associated error message: +<p> +<pre> + leveldb::Status s = ...; + if (!s.ok()) cerr << s.ToString() << endl; +</pre> +<h1>Closing A Database</h1> +<p> +When you are done with a database, just delete the database object. +Example: +<p> +<pre> + ... open the db as described above ... + ... do something with db ... + delete db; +</pre> +<h1>Reads And Writes</h1> +<p> +The database provides <code>Put</code>, <code>Delete</code>, and <code>Get</code> methods to +modify/query the database. For example, the following code +moves the value stored under key1 to key2. +<pre> + std::string value; + leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value); + if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value); + if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1); +</pre> + +<h1>Atomic Updates</h1> +<p> +Note that if the process dies after the Put of key2 but before the +delete of key1, the same value may be left stored under multiple keys. +Such problems can be avoided by using the <code>WriteBatch</code> class to +atomically apply a set of updates: +<p> +<pre> + #include "leveldb/write_batch.h" + ... + std::string value; + leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value); + if (s.ok()) { + leveldb::WriteBatch batch; + batch.Delete(key1); + batch.Put(key2, value); + s = db->Write(leveldb::WriteOptions(), &batch); + } +</pre> +The <code>WriteBatch</code> holds a sequence of edits to be made to the database, +and these edits within the batch are applied in order. Note that we +called <code>Delete</code> before <code>Put</code> so that if <code>key1</code> is identical to <code>key2</code>, +we do not end up erroneously dropping the value entirely. +<p> +Apart from its atomicity benefits, <code>WriteBatch</code> may also be used to +speed up bulk updates by placing lots of individual mutations into the +same batch. + +<h1>Synchronous Writes</h1> +By default, each write to <code>leveldb</code> is asynchronous: it +returns after pushing the write from the process into the operating +system. The transfer from operating system memory to the underlying +persistent storage happens asynchronously. The <code>sync</code> flag +can be turned on for a particular write to make the write operation +not return until the data being written has been pushed all the way to +persistent storage. (On Posix systems, this is implemented by calling +either <code>fsync(...)</code> or <code>fdatasync(...)</code> or +<code>msync(..., MS_SYNC)</code> before the write operation returns.) +<pre> + leveldb::WriteOptions write_options; + write_options.sync = true; + db->Put(write_options, ...); +</pre> +Asynchronous writes are often more than a thousand times as fast as +synchronous writes. The downside of asynchronous writes is that a +crash of the machine may cause the last few updates to be lost. Note +that a crash of just the writing process (i.e., not a reboot) will not +cause any loss since even when <code>sync</code> is false, an update +is pushed from the process memory into the operating system before it +is considered done. + +<p> +Asynchronous writes can often be used safely. For example, when +loading a large amount of data into the database you can handle lost +updates by restarting the bulk load after a crash. A hybrid scheme is +also possible where every Nth write is synchronous, and in the event +of a crash, the bulk load is restarted just after the last synchronous +write finished by the previous run. (The synchronous write can update +a marker that describes where to restart on a crash.) + +<p> +<code>WriteBatch</code> provides an alternative to asynchronous writes. +Multiple updates may be placed in the same <code>WriteBatch</code> and +applied together using a synchronous write (i.e., +<code>write_options.sync</code> is set to true). The extra cost of +the synchronous write will be amortized across all of the writes in +the batch. + +<p> +<h1>Concurrency</h1> +<p> +A database may only be opened by one process at a time. +The <code>leveldb</code> implementation acquires a lock from the +operating system to prevent misuse. Within a single process, the +same <code>leveldb::DB</code> object may be safely shared by multiple +concurrent threads. I.e., different threads may write into or fetch +iterators or call <code>Get</code> on the same database without any +external synchronization (the leveldb implementation will +automatically do the required synchronization). However other objects +(like Iterator and WriteBatch) may require external synchronization. +If two threads share such an object, they must protect access to it +using their own locking protocol. More details are available in +the public header files. +<p> +<h1>Iteration</h1> +<p> +The following example demonstrates how to print all key,value pairs +in a database. +<p> +<pre> + leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions()); + for (it->SeekToFirst(); it->Valid(); it->Next()) { + cout << it->key().ToString() << ": " << it->value().ToString() << endl; + } + assert(it->status().ok()); // Check for any errors found during the scan + delete it; +</pre> +The following variation shows how to process just the keys in the +range <code>[start,limit)</code>: +<p> +<pre> + for (it->Seek(start); + it->Valid() && it->key().ToString() < limit; + it->Next()) { + ... + } +</pre> +You can also process entries in reverse order. (Caveat: reverse +iteration may be somewhat slower than forward iteration.) +<p> +<pre> + for (it->SeekToLast(); it->Valid(); it->Prev()) { + ... + } +</pre> +<h1>Snapshots</h1> +<p> +Snapshots provide consistent read-only views over the entire state of +the key-value store. <code>ReadOptions::snapshot</code> may be non-NULL to indicate +that a read should operate on a particular version of the DB state. +If <code>ReadOptions::snapshot</code> is NULL, the read will operate on an +implicit snapshot of the current state. +<p> +Snapshots are created by the DB::GetSnapshot() method: +<p> +<pre> + leveldb::ReadOptions options; + options.snapshot = db->GetSnapshot(); + ... apply some updates to db ... + leveldb::Iterator* iter = db->NewIterator(options); + ... read using iter to view the state when the snapshot was created ... + delete iter; + db->ReleaseSnapshot(options.snapshot); +</pre> +Note that when a snapshot is no longer needed, it should be released +using the DB::ReleaseSnapshot interface. This allows the +implementation to get rid of state that was being maintained just to +support reading as of that snapshot. +<h1>Slice</h1> +<p> +The return value of the <code>it->key()</code> and <code>it->value()</code> calls above +are instances of the <code>leveldb::Slice</code> type. <code>Slice</code> is a simple +structure that contains a length and a pointer to an external byte +array. Returning a <code>Slice</code> is a cheaper alternative to returning a +<code>std::string</code> since we do not need to copy potentially large keys and +values. In addition, <code>leveldb</code> methods do not return null-terminated +C-style strings since <code>leveldb</code> keys and values are allowed to +contain '\0' bytes. +<p> +C++ strings and null-terminated C-style strings can be easily converted +to a Slice: +<p> +<pre> + leveldb::Slice s1 = "hello"; + + std::string str("world"); + leveldb::Slice s2 = str; +</pre> +A Slice can be easily converted back to a C++ string: +<pre> + std::string str = s1.ToString(); + assert(str == std::string("hello")); +</pre> +Be careful when using Slices since it is up to the caller to ensure that +the external byte array into which the Slice points remains live while +the Slice is in use. For example, the following is buggy: +<p> +<pre> + leveldb::Slice slice; + if (...) { + std::string str = ...; + slice = str; + } + Use(slice); +</pre> +When the <code>if</code> statement goes out of scope, <code>str</code> will be destroyed and the +backing storage for <code>slice</code> will disappear. +<p> +<h1>Comparators</h1> +<p> +The preceding examples used the default ordering function for key, +which orders bytes lexicographically. You can however supply a custom +comparator when opening a database. For example, suppose each +database key consists of two numbers and we should sort by the first +number, breaking ties by the second number. First, define a proper +subclass of <code>leveldb::Comparator</code> that expresses these rules: +<p> +<pre> + class TwoPartComparator : public leveldb::Comparator { + public: + // Three-way comparison function: + // if a < b: negative result + // if a > b: positive result + // else: zero result + int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const { + int a1, a2, b1, b2; + ParseKey(a, &a1, &a2); + ParseKey(b, &b1, &b2); + if (a1 < b1) return -1; + if (a1 > b1) return +1; + if (a2 < b2) return -1; + if (a2 > b2) return +1; + return 0; + } + + // Ignore the following methods for now: + const char* Name() const { return "TwoPartComparator"; } + void FindShortestSeparator(std::string*, const leveldb::Slice&) const { } + void FindShortSuccessor(std::string*) const { } + }; +</pre> +Now create a database using this custom comparator: +<p> +<pre> + TwoPartComparator cmp; + leveldb::DB* db; + leveldb::Options options; + options.create_if_missing = true; + options.comparator = &cmp; + leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); + ... +</pre> +<h2>Backwards compatibility</h2> +<p> +The result of the comparator's <code>Name</code> method is attached to the +database when it is created, and is checked on every subsequent +database open. If the name changes, the <code>leveldb::DB::Open</code> call will +fail. Therefore, change the name if and only if the new key format +and comparison function are incompatible with existing databases, and +it is ok to discard the contents of all existing databases. +<p> +You can however still gradually evolve your key format over time with +a little bit of pre-planning. For example, you could store a version +number at the end of each key (one byte should suffice for most uses). +When you wish to switch to a new key format (e.g., adding an optional +third part to the keys processed by <code>TwoPartComparator</code>), +(a) keep the same comparator name (b) increment the version number +for new keys (c) change the comparator function so it uses the +version numbers found in the keys to decide how to interpret them. +<p> +<h1>Performance</h1> +<p> +Performance can be tuned by changing the default values of the +types defined in <code>include/leveldb/options.h</code>. + +<p> +<h2>Block size</h2> +<p> +<code>leveldb</code> groups adjacent keys together into the same block and such a +block is the unit of transfer to and from persistent storage. The +default block size is approximately 4096 uncompressed bytes. +Applications that mostly do bulk scans over the contents of the +database may wish to increase this size. Applications that do a lot +of point reads of small values may wish to switch to a smaller block +size if performance measurements indicate an improvement. There isn't +much benefit in using blocks smaller than one kilobyte, or larger than +a few megabytes. Also note that compression will be more effective +with larger block sizes. +<p> +<h2>Compression</h2> +<p> +Each block is individually compressed before being written to +persistent storage. Compression is on by default since the default +compression method is very fast, and is automatically disabled for +uncompressible data. In rare cases, applications may want to disable +compression entirely, but should only do so if benchmarks show a +performance improvement: +<p> +<pre> + leveldb::Options options; + options.compression = leveldb::kNoCompression; + ... leveldb::DB::Open(options, name, ...) .... +</pre> +<h2>Cache</h2> +<p> +The contents of the database are stored in a set of files in the +filesystem and each file stores a sequence of compressed blocks. If +<code>options.cache</code> is non-NULL, it is used to cache frequently used +uncompressed block contents. +<p> +<pre> + #include "leveldb/cache.h" + + leveldb::Options options; + options.cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache + leveldb::DB* db; + leveldb::DB::Open(options, name, &db); + ... use the db ... + delete db + delete options.cache; +</pre> +Note that the cache holds uncompressed data, and therefore it should +be sized according to application level data sizes, without any +reduction from compression. (Caching of compressed blocks is left to +the operating system buffer cache, or any custom <code>Env</code> +implementation provided by the client.) +<p> +When performing a bulk read, the application may wish to disable +caching so that the data processed by the bulk read does not end up +displacing most of the cached contents. A per-iterator option can be +used to achieve this: +<p> +<pre> + leveldb::ReadOptions options; + options.fill_cache = false; + leveldb::Iterator* it = db->NewIterator(options); + for (it->SeekToFirst(); it->Valid(); it->Next()) { + ... + } +</pre> +<h2>Key Layout</h2> +<p> +Note that the unit of disk transfer and caching is a block. Adjacent +keys (according to the database sort order) will usually be placed in +the same block. Therefore the application can improve its performance +by placing keys that are accessed together near each other and placing +infrequently used keys in a separate region of the key space. +<p> +For example, suppose we are implementing a simple file system on top +of <code>leveldb</code>. The types of entries we might wish to store are: +<p> +<pre> + filename -> permission-bits, length, list of file_block_ids + file_block_id -> data +</pre> +We might want to prefix <code>filename</code> keys with one letter (say '/') and the +<code>file_block_id</code> keys with a different letter (say '0') so that scans +over just the metadata do not force us to fetch and cache bulky file +contents. +<p> +<h2>Filters</h2> +<p> +Because of the way <code>leveldb</code> data is organized on disk, +a single <code>Get()</code> call may involve multiple reads from disk. +The optional <code>FilterPolicy</code> mechanism can be used to reduce +the number of disk reads substantially. +<pre> + leveldb::Options options; + options.filter_policy = NewBloomFilter(10); + leveldb::DB* db; + leveldb::DB::Open(options, "/tmp/testdb", &db); + ... use the database ... + delete db; + delete options.filter_policy; +</pre> +The preceding code associates a +<a href="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</a> +based filtering policy with the database. Bloom filter based +filtering relies on keeping some number of bits of data in memory per +key (in this case 10 bits per key since that is the argument we passed +to NewBloomFilter). This filter will reduce the number of unnecessary +disk reads needed for <code>Get()</code> calls by a factor of +approximately a 100. Increasing the bits per key will lead to a +larger reduction at the cost of more memory usage. We recommend that +applications whose working set does not fit in memory and that do a +lot of random reads set a filter policy. +<p> +If you are using a custom comparator, you should ensure that the filter +policy you are using is compatible with your comparator. For example, +consider a comparator that ignores trailing spaces when comparing keys. +<code>NewBloomFilter</code> must not be used with such a comparator. +Instead, the application should provide a custom filter policy that +also ignores trailing spaces. For example: +<pre> + class CustomFilterPolicy : public leveldb::FilterPolicy { + private: + FilterPolicy* builtin_policy_; + public: + CustomFilterPolicy() : builtin_policy_(NewBloomFilter(10)) { } + ~CustomFilterPolicy() { delete builtin_policy_; } + + const char* Name() const { return "IgnoreTrailingSpacesFilter"; } + + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + // Use builtin bloom filter code after removing trailing spaces + std::vector<Slice> trimmed(n); + for (int i = 0; i < n; i++) { + trimmed[i] = RemoveTrailingSpaces(keys[i]); + } + return builtin_policy_->CreateFilter(&trimmed[i], n, dst); + } + + bool KeyMayMatch(const Slice& key, const Slice& filter) const { + // Use builtin bloom filter code after removing trailing spaces + return builtin_policy_->KeyMayMatch(RemoveTrailingSpaces(key), filter); + } + }; +</pre> +<p> +Advanced applications may provide a filter policy that does not use +a bloom filter but uses some other mechanism for summarizing a set +of keys. See <code>leveldb/filter_policy.h</code> for detail. +<p> +<h1>Checksums</h1> +<p> +<code>leveldb</code> associates checksums with all data it stores in the file system. +There are two separate controls provided over how aggressively these +checksums are verified: +<p> +<ul> +<li> <code>ReadOptions::verify_checksums</code> may be set to true to force + checksum verification of all data that is read from the file system on + behalf of a particular read. By default, no such verification is + done. +<p> +<li> <code>Options::paranoid_checks</code> may be set to true before opening a + database to make the database implementation raise an error as soon as + it detects an internal corruption. Depending on which portion of the + database has been corrupted, the error may be raised when the database + is opened, or later by another database operation. By default, + paranoid checking is off so that the database can be used even if + parts of its persistent storage have been corrupted. +<p> + If a database is corrupted (perhaps it cannot be opened when + paranoid checking is turned on), the <code>leveldb::RepairDB</code> function + may be used to recover as much of the data as possible +<p> +</ul> +<h1>Approximate Sizes</h1> +<p> +The <code>GetApproximateSizes</code> method can used to get the approximate +number of bytes of file system space used by one or more key ranges. +<p> +<pre> + leveldb::Range ranges[2]; + ranges[0] = leveldb::Range("a", "c"); + ranges[1] = leveldb::Range("x", "z"); + uint64_t sizes[2]; + leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes); +</pre> +The preceding call will set <code>sizes[0]</code> to the approximate number of +bytes of file system space used by the key range <code>[a..c)</code> and +<code>sizes[1]</code> to the approximate number of bytes used by the key range +<code>[x..z)</code>. +<p> +<h1>Environment</h1> +<p> +All file operations (and other operating system calls) issued by the +<code>leveldb</code> implementation are routed through a <code>leveldb::Env</code> object. +Sophisticated clients may wish to provide their own <code>Env</code> +implementation to get better control. For example, an application may +introduce artificial delays in the file IO paths to limit the impact +of <code>leveldb</code> on other activities in the system. +<p> +<pre> + class SlowEnv : public leveldb::Env { + .. implementation of the Env interface ... + }; + + SlowEnv env; + leveldb::Options options; + options.env = &env; + Status s = leveldb::DB::Open(options, ...); +</pre> +<h1>Porting</h1> +<p> +<code>leveldb</code> may be ported to a new platform by providing platform +specific implementations of the types/methods/functions exported by +<code>leveldb/port/port.h</code>. See <code>leveldb/port/port_example.h</code> for more +details. +<p> +In addition, the new platform may need a new default <code>leveldb::Env</code> +implementation. See <code>leveldb/util/env_posix.h</code> for an example. + +<h1>Other Information</h1> + +<p> +Details about the <code>leveldb</code> implementation may be found in +the following documents: +<ul> +<li> <a href="impl.html">Implementation notes</a> +<li> <a href="table_format.txt">Format of an immutable Table file</a> +<li> <a href="log_format.txt">Format of a log file</a> +</ul> + +</body> +</html> diff --git a/src/leveldb/doc/log_format.txt b/src/leveldb/doc/log_format.txt new file mode 100644 index 0000000000..3a0414b65a --- /dev/null +++ b/src/leveldb/doc/log_format.txt @@ -0,0 +1,75 @@ +The log file contents are a sequence of 32KB blocks. The only +exception is that the tail of the file may contain a partial block. + +Each block consists of a sequence of records: + block := record* trailer? + record := + checksum: uint32 // crc32c of type and data[] + length: uint16 + type: uint8 // One of FULL, FIRST, MIDDLE, LAST + data: uint8[length] + +A record never starts within the last six bytes of a block (since it +won't fit). Any leftover bytes here form the trailer, which must +consist entirely of zero bytes and must be skipped by readers. + +Aside: if exactly seven bytes are left in the current block, and a new +non-zero length record is added, the writer must emit a FIRST record +(which contains zero bytes of user data) to fill up the trailing seven +bytes of the block and then emit all of the user data in subsequent +blocks. + +More types may be added in the future. Some Readers may skip record +types they do not understand, others may report that some data was +skipped. + +FULL == 1 +FIRST == 2 +MIDDLE == 3 +LAST == 4 + +The FULL record contains the contents of an entire user record. + +FIRST, MIDDLE, LAST are types used for user records that have been +split into multiple fragments (typically because of block boundaries). +FIRST is the type of the first fragment of a user record, LAST is the +type of the last fragment of a user record, and MID is the type of all +interior fragments of a user record. + +Example: consider a sequence of user records: + A: length 1000 + B: length 97270 + C: length 8000 +A will be stored as a FULL record in the first block. + +B will be split into three fragments: first fragment occupies the rest +of the first block, second fragment occupies the entirety of the +second block, and the third fragment occupies a prefix of the third +block. This will leave six bytes free in the third block, which will +be left empty as the trailer. + +C will be stored as a FULL record in the fourth block. + +=================== + +Some benefits over the recordio format: + +(1) We do not need any heuristics for resyncing - just go to next +block boundary and scan. If there is a corruption, skip to the next +block. As a side-benefit, we do not get confused when part of the +contents of one log file are embedded as a record inside another log +file. + +(2) Splitting at approximate boundaries (e.g., for mapreduce) is +simple: find the next block boundary and skip records until we +hit a FULL or FIRST record. + +(3) We do not need extra buffering for large records. + +Some downsides compared to recordio format: + +(1) No packing of tiny records. This could be fixed by adding a new +record type, so it is a shortcoming of the current implementation, +not necessarily the format. + +(2) No compression. Again, this could be fixed by adding new record types. diff --git a/src/leveldb/doc/table_format.txt b/src/leveldb/doc/table_format.txt new file mode 100644 index 0000000000..d0f3065ed0 --- /dev/null +++ b/src/leveldb/doc/table_format.txt @@ -0,0 +1,102 @@ +File format +=========== + + <beginning_of_file> + [data block 1] + [data block 2] + ... + [data block N] + [meta block 1] + ... + [meta block K] + [metaindex block] + [index block] + [Footer] (fixed size; starts at file_size - sizeof(Footer)) + <end_of_file> + +The file contains internal pointers. Each such pointer is called +a BlockHandle and contains the following information: + offset: varint64 + size: varint64 + +(1) The sequence of key/value pairs in the file are stored in sorted +order and partitioned into a sequence of data blocks. These blocks +come one after another at the beginning of the file. Each data block +is formatted according to the code in block_builder.cc, and then +optionally compressed. + +(2) After the data blocks we store a bunch of meta blocks. The +supported meta block types are described below. More meta block types +may be added in the future. Each meta block is again formatted using +block_builder.cc and then optionally compressed. + +(3) A "metaindex" block. It contains one entry for every other meta +block where the key is the name of the meta block and the value is a +BlockHandle pointing to that meta block. + +(4) An "index" block. This block contains one entry per data block, +where the key is a string >= last key in that data block and before +the first key in the successive data block. The value is the +BlockHandle for the data block. + +(6) At the very end of the file is a fixed length footer that contains +the BlockHandle of the metaindex and index blocks as well as a magic number. + metaindex_handle: char[p]; // Block handle for metaindex + index_handle: char[q]; // Block handle for index + padding: char[40-p-q]; // 0 bytes to make fixed length + // (40==2*BlockHandle::kMaxEncodedLength) + magic: fixed64; // == 0xdb4775248b80fb57 + +"filter" Meta Block +------------------- + +If a "FilterPolicy" was specified when the database was opened, a +filter block is stored in each table. The "metaindex" block contains +an entry that maps from "filter.<N>" to the BlockHandle for the filter +block where "<N>" is the string returned by the filter policy's +"Name()" method. + +The filter block stores a sequence of filters, where filter i contains +the output of FilterPolicy::CreateFilter() on all keys that are stored +in a block whose file offset falls within the range + + [ i*base ... (i+1)*base-1 ] + +Currently, "base" is 2KB. So for example, if blocks X and Y start in +the range [ 0KB .. 2KB-1 ], all of the keys in X and Y will be +converted to a filter by calling FilterPolicy::CreateFilter(), and the +resulting filter will be stored as the first filter in the filter +block. + +The filter block is formatted as follows: + + [filter 0] + [filter 1] + [filter 2] + ... + [filter N-1] + + [offset of filter 0] : 4 bytes + [offset of filter 1] : 4 bytes + [offset of filter 2] : 4 bytes + ... + [offset of filter N-1] : 4 bytes + + [offset of beginning of offset array] : 4 bytes + lg(base) : 1 byte + +The offset array at the end of the filter block allows efficient +mapping from a data block offset to the corresponding filter. + +"stats" Meta Block +------------------ + +This meta block contains a bunch of stats. The key is the name +of the statistic. The value contains the statistic. +TODO(postrelease): record following stats. + data size + index size + key size (uncompressed) + value size (uncompressed) + number of entries + number of data blocks diff --git a/src/leveldb/helpers/memenv/memenv.cc b/src/leveldb/helpers/memenv/memenv.cc new file mode 100644 index 0000000000..2082083b3f --- /dev/null +++ b/src/leveldb/helpers/memenv/memenv.cc @@ -0,0 +1,374 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "port/port.h" +#include "util/mutexlock.h" +#include <map> +#include <string.h> +#include <string> +#include <vector> + +namespace leveldb { + +namespace { + +class FileState { + public: + // FileStates are reference counted. The initial reference count is zero + // and the caller must call Ref() at least once. + FileState() : refs_(0), size_(0) {} + + // Increase the reference count. + void Ref() { + MutexLock lock(&refs_mutex_); + ++refs_; + } + + // Decrease the reference count. Delete if this is the last reference. + void Unref() { + bool do_delete = false; + + { + MutexLock lock(&refs_mutex_); + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + do_delete = true; + } + } + + if (do_delete) { + delete this; + } + } + + uint64_t Size() const { return size_; } + + Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { + if (offset > size_) { + return Status::IOError("Offset greater than file size."); + } + const uint64_t available = size_ - offset; + if (n > available) { + n = available; + } + if (n == 0) { + *result = Slice(); + return Status::OK(); + } + + size_t block = offset / kBlockSize; + size_t block_offset = offset % kBlockSize; + + if (n <= kBlockSize - block_offset) { + // The requested bytes are all in the first block. + *result = Slice(blocks_[block] + block_offset, n); + return Status::OK(); + } + + size_t bytes_to_copy = n; + char* dst = scratch; + + while (bytes_to_copy > 0) { + size_t avail = kBlockSize - block_offset; + if (avail > bytes_to_copy) { + avail = bytes_to_copy; + } + memcpy(dst, blocks_[block] + block_offset, avail); + + bytes_to_copy -= avail; + dst += avail; + block++; + block_offset = 0; + } + + *result = Slice(scratch, n); + return Status::OK(); + } + + Status Append(const Slice& data) { + const char* src = data.data(); + size_t src_len = data.size(); + + while (src_len > 0) { + size_t avail; + size_t offset = size_ % kBlockSize; + + if (offset != 0) { + // There is some room in the last block. + avail = kBlockSize - offset; + } else { + // No room in the last block; push new one. + blocks_.push_back(new char[kBlockSize]); + avail = kBlockSize; + } + + if (avail > src_len) { + avail = src_len; + } + memcpy(blocks_.back() + offset, src, avail); + src_len -= avail; + src += avail; + size_ += avail; + } + + return Status::OK(); + } + + private: + // Private since only Unref() should be used to delete it. + ~FileState() { + for (std::vector<char*>::iterator i = blocks_.begin(); i != blocks_.end(); + ++i) { + delete [] *i; + } + } + + // No copying allowed. + FileState(const FileState&); + void operator=(const FileState&); + + port::Mutex refs_mutex_; + int refs_; // Protected by refs_mutex_; + + // The following fields are not protected by any mutex. They are only mutable + // while the file is being written, and concurrent access is not allowed + // to writable files. + std::vector<char*> blocks_; + uint64_t size_; + + enum { kBlockSize = 8 * 1024 }; +}; + +class SequentialFileImpl : public SequentialFile { + public: + explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) { + file_->Ref(); + } + + ~SequentialFileImpl() { + file_->Unref(); + } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s = file_->Read(pos_, n, result, scratch); + if (s.ok()) { + pos_ += result->size(); + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (pos_ > file_->Size()) { + return Status::IOError("pos_ > file_->Size()"); + } + const size_t available = file_->Size() - pos_; + if (n > available) { + n = available; + } + pos_ += n; + return Status::OK(); + } + + private: + FileState* file_; + size_t pos_; +}; + +class RandomAccessFileImpl : public RandomAccessFile { + public: + explicit RandomAccessFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~RandomAccessFileImpl() { + file_->Unref(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + return file_->Read(offset, n, result, scratch); + } + + private: + FileState* file_; +}; + +class WritableFileImpl : public WritableFile { + public: + WritableFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~WritableFileImpl() { + file_->Unref(); + } + + virtual Status Append(const Slice& data) { + return file_->Append(data); + } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + private: + FileState* file_; +}; + +class InMemoryEnv : public EnvWrapper { + public: + explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { } + + virtual ~InMemoryEnv() { + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + i->second->Unref(); + } + } + + // Partial implementation of the Env interface. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new SequentialFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new RandomAccessFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) != file_map_.end()) { + DeleteFileInternal(fname); + } + + FileState* file = new FileState(); + file->Ref(); + file_map_[fname] = file; + + *result = new WritableFileImpl(file); + return Status::OK(); + } + + virtual bool FileExists(const std::string& fname) { + MutexLock lock(&mutex_); + return file_map_.find(fname) != file_map_.end(); + } + + virtual Status GetChildren(const std::string& dir, + std::vector<std::string>* result) { + MutexLock lock(&mutex_); + result->clear(); + + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + const std::string& filename = i->first; + + if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' && + Slice(filename).starts_with(Slice(dir))) { + result->push_back(filename.substr(dir.size() + 1)); + } + } + + return Status::OK(); + } + + void DeleteFileInternal(const std::string& fname) { + if (file_map_.find(fname) == file_map_.end()) { + return; + } + + file_map_[fname]->Unref(); + file_map_.erase(fname); + } + + virtual Status DeleteFile(const std::string& fname) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + DeleteFileInternal(fname); + return Status::OK(); + } + + virtual Status CreateDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status DeleteDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + *file_size = file_map_[fname]->Size(); + return Status::OK(); + } + + virtual Status RenameFile(const std::string& src, + const std::string& target) { + MutexLock lock(&mutex_); + if (file_map_.find(src) == file_map_.end()) { + return Status::IOError(src, "File not found"); + } + + DeleteFileInternal(target); + file_map_[target] = file_map_[src]; + file_map_.erase(src); + return Status::OK(); + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = new FileLock; + return Status::OK(); + } + + virtual Status UnlockFile(FileLock* lock) { + delete lock; + return Status::OK(); + } + + virtual Status GetTestDirectory(std::string* path) { + *path = "/test"; + return Status::OK(); + } + + private: + // Map from filenames to FileState objects, representing a simple file system. + typedef std::map<std::string, FileState*> FileSystem; + port::Mutex mutex_; + FileSystem file_map_; // Protected by mutex_. +}; + +} // namespace + +Env* NewMemEnv(Env* base_env) { + return new InMemoryEnv(base_env); +} + +} // namespace leveldb diff --git a/src/leveldb/helpers/memenv/memenv.h b/src/leveldb/helpers/memenv/memenv.h new file mode 100644 index 0000000000..03b88de761 --- /dev/null +++ b/src/leveldb/helpers/memenv/memenv.h @@ -0,0 +1,20 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ +#define STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ + +namespace leveldb { + +class Env; + +// Returns a new environment that stores its data in memory and delegates +// all non-file-storage tasks to base_env. The caller must delete the result +// when it is no longer needed. +// *base_env must remain live while the result is in use. +Env* NewMemEnv(Env* base_env); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ diff --git a/src/leveldb/helpers/memenv/memenv_test.cc b/src/leveldb/helpers/memenv/memenv_test.cc new file mode 100644 index 0000000000..a44310fed8 --- /dev/null +++ b/src/leveldb/helpers/memenv/memenv_test.cc @@ -0,0 +1,232 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "db/db_impl.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "util/testharness.h" +#include <string> +#include <vector> + +namespace leveldb { + +class MemEnvTest { + public: + Env* env_; + + MemEnvTest() + : env_(NewMemEnv(Env::Default())) { + } + ~MemEnvTest() { + delete env_; + } +}; + +TEST(MemEnvTest, Basics) { + uint64_t file_size; + WritableFile* writable_file; + std::vector<std::string> children; + + ASSERT_OK(env_->CreateDir("/dir")); + + // Check that the directory is empty. + ASSERT_TRUE(!env_->FileExists("/dir/non_existent")); + ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok()); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + + // Create a file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + delete writable_file; + + // Check that the file exists. + ASSERT_TRUE(env_->FileExists("/dir/f")); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(0, file_size); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(1, children.size()); + ASSERT_EQ("f", children[0]); + + // Write to the file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("abc")); + delete writable_file; + + // Check for expected size. + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(3, file_size); + + // Check that renaming works. + ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok()); + ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/f")); + ASSERT_TRUE(env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetFileSize("/dir/g", &file_size)); + ASSERT_EQ(3, file_size); + + // Check that opening non-existent file fails. + SequentialFile* seq_file; + RandomAccessFile* rand_file; + ASSERT_TRUE(!env_->NewSequentialFile("/dir/non_existent", &seq_file).ok()); + ASSERT_TRUE(!seq_file); + ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file).ok()); + ASSERT_TRUE(!rand_file); + + // Check that deleting works. + ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok()); + ASSERT_OK(env_->DeleteFile("/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + ASSERT_OK(env_->DeleteDir("/dir")); +} + +TEST(MemEnvTest, ReadWrite) { + WritableFile* writable_file; + SequentialFile* seq_file; + RandomAccessFile* rand_file; + Slice result; + char scratch[100]; + + ASSERT_OK(env_->CreateDir("/dir")); + + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("hello ")); + ASSERT_OK(writable_file->Append("world")); + delete writable_file; + + // Read sequentially. + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(seq_file->Skip(1)); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. + ASSERT_EQ(0, result.size()); + ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. + ASSERT_OK(seq_file->Read(1000, &result, scratch)); + ASSERT_EQ(0, result.size()); + delete seq_file; + + // Random reads. + ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file)); + ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". + ASSERT_EQ(0, result.compare("d")); + + // Too high offset. + ASSERT_TRUE(!rand_file->Read(1000, 5, &result, scratch).ok()); + delete rand_file; +} + +TEST(MemEnvTest, Locks) { + FileLock* lock; + + // These are no-ops, but we test they return success. + ASSERT_OK(env_->LockFile("some file", &lock)); + ASSERT_OK(env_->UnlockFile(lock)); +} + +TEST(MemEnvTest, Misc) { + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + ASSERT_TRUE(!test_dir.empty()); + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/a/b", &writable_file)); + + // These are no-ops, but we test they return success. + ASSERT_OK(writable_file->Sync()); + ASSERT_OK(writable_file->Flush()); + ASSERT_OK(writable_file->Close()); + delete writable_file; +} + +TEST(MemEnvTest, LargeWrite) { + const size_t kWriteSize = 300 * 1024; + char* scratch = new char[kWriteSize * 2]; + + std::string write_data; + for (size_t i = 0; i < kWriteSize; ++i) { + write_data.append(1, static_cast<char>(i)); + } + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("foo")); + ASSERT_OK(writable_file->Append(write_data)); + delete writable_file; + + SequentialFile* seq_file; + Slice result; + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". + ASSERT_EQ(0, result.compare("foo")); + + size_t read = 0; + std::string read_data; + while (read < kWriteSize) { + ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch)); + read_data.append(result.data(), result.size()); + read += result.size(); + } + ASSERT_TRUE(write_data == read_data); + delete seq_file; + delete [] scratch; +} + +TEST(MemEnvTest, DBTest) { + Options options; + options.create_if_missing = true; + options.env = env_; + DB* db; + + const Slice keys[] = {Slice("aaa"), Slice("bbb"), Slice("ccc")}; + const Slice vals[] = {Slice("foo"), Slice("bar"), Slice("baz")}; + + ASSERT_OK(DB::Open(options, "/dir/db", &db)); + for (size_t i = 0; i < 3; ++i) { + ASSERT_OK(db->Put(WriteOptions(), keys[i], vals[i])); + } + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + Iterator* iterator = db->NewIterator(ReadOptions()); + iterator->SeekToFirst(); + for (size_t i = 0; i < 3; ++i) { + ASSERT_TRUE(iterator->Valid()); + ASSERT_TRUE(keys[i] == iterator->key()); + ASSERT_TRUE(vals[i] == iterator->value()); + iterator->Next(); + } + ASSERT_TRUE(!iterator->Valid()); + delete iterator; + + DBImpl* dbi = reinterpret_cast<DBImpl*>(db); + ASSERT_OK(dbi->TEST_CompactMemTable()); + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + delete db; +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/include/leveldb/c.h b/src/leveldb/include/leveldb/c.h new file mode 100644 index 0000000000..70e3cc6528 --- /dev/null +++ b/src/leveldb/include/leveldb/c.h @@ -0,0 +1,275 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. + + C bindings for leveldb. May be useful as a stable ABI that can be + used by programs that keep leveldb in a shared library, or for + a JNI api. + + Does not support: + . getters for the option types + . custom comparators that implement key shortening + . capturing post-write-snapshot + . custom iter, db, env, cache implementations using just the C bindings + + Some conventions: + + (1) We expose just opaque struct pointers and functions to clients. + This allows us to change internal representations without having to + recompile clients. + + (2) For simplicity, there is no equivalent to the Slice type. Instead, + the caller has to pass the pointer and length as separate + arguments. + + (3) Errors are represented by a null-terminated c string. NULL + means no error. All operations that can raise an error are passed + a "char** errptr" as the last argument. One of the following must + be true on entry: + *errptr == NULL + *errptr points to a malloc()ed null-terminated error message + On success, a leveldb routine leaves *errptr unchanged. + On failure, leveldb frees the old value of *errptr and + set *errptr to a malloc()ed error message. + + (4) Bools have the type unsigned char (0 == false; rest == true) + + (5) All of the pointer arguments must be non-NULL. +*/ + +#ifndef STORAGE_LEVELDB_INCLUDE_C_H_ +#define STORAGE_LEVELDB_INCLUDE_C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> + +/* Exported types */ + +typedef struct leveldb_t leveldb_t; +typedef struct leveldb_cache_t leveldb_cache_t; +typedef struct leveldb_comparator_t leveldb_comparator_t; +typedef struct leveldb_env_t leveldb_env_t; +typedef struct leveldb_filelock_t leveldb_filelock_t; +typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; +typedef struct leveldb_iterator_t leveldb_iterator_t; +typedef struct leveldb_logger_t leveldb_logger_t; +typedef struct leveldb_options_t leveldb_options_t; +typedef struct leveldb_randomfile_t leveldb_randomfile_t; +typedef struct leveldb_readoptions_t leveldb_readoptions_t; +typedef struct leveldb_seqfile_t leveldb_seqfile_t; +typedef struct leveldb_snapshot_t leveldb_snapshot_t; +typedef struct leveldb_writablefile_t leveldb_writablefile_t; +typedef struct leveldb_writebatch_t leveldb_writebatch_t; +typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; + +/* DB operations */ + +extern leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_close(leveldb_t* db); + +extern void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr); + +extern void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr); + +extern void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr); + +/* Returns NULL if not found. A malloc()ed array otherwise. + Stores the length of the array in *vallen. */ +extern char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr); + +extern leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options); + +extern const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db); + +extern void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot); + +/* Returns NULL if property name is unknown. + Else returns a pointer to a malloc()-ed null-terminated value. */ +extern char* leveldb_property_value( + leveldb_t* db, + const char* propname); + +extern void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes); + +extern void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len); + +/* Management operations */ + +extern void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +/* Iterator */ + +extern void leveldb_iter_destroy(leveldb_iterator_t*); +extern unsigned char leveldb_iter_valid(const leveldb_iterator_t*); +extern void leveldb_iter_seek_to_first(leveldb_iterator_t*); +extern void leveldb_iter_seek_to_last(leveldb_iterator_t*); +extern void leveldb_iter_seek(leveldb_iterator_t*, const char* k, size_t klen); +extern void leveldb_iter_next(leveldb_iterator_t*); +extern void leveldb_iter_prev(leveldb_iterator_t*); +extern const char* leveldb_iter_key(const leveldb_iterator_t*, size_t* klen); +extern const char* leveldb_iter_value(const leveldb_iterator_t*, size_t* vlen); +extern void leveldb_iter_get_error(const leveldb_iterator_t*, char** errptr); + +/* Write batch */ + +extern leveldb_writebatch_t* leveldb_writebatch_create(); +extern void leveldb_writebatch_destroy(leveldb_writebatch_t*); +extern void leveldb_writebatch_clear(leveldb_writebatch_t*); +extern void leveldb_writebatch_put( + leveldb_writebatch_t*, + const char* key, size_t klen, + const char* val, size_t vlen); +extern void leveldb_writebatch_delete( + leveldb_writebatch_t*, + const char* key, size_t klen); +extern void leveldb_writebatch_iterate( + leveldb_writebatch_t*, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)); + +/* Options */ + +extern leveldb_options_t* leveldb_options_create(); +extern void leveldb_options_destroy(leveldb_options_t*); +extern void leveldb_options_set_comparator( + leveldb_options_t*, + leveldb_comparator_t*); +extern void leveldb_options_set_filter_policy( + leveldb_options_t*, + leveldb_filterpolicy_t*); +extern void leveldb_options_set_create_if_missing( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_error_if_exists( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_paranoid_checks( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); +extern void leveldb_options_set_info_log(leveldb_options_t*, leveldb_logger_t*); +extern void leveldb_options_set_write_buffer_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_max_open_files(leveldb_options_t*, int); +extern void leveldb_options_set_cache(leveldb_options_t*, leveldb_cache_t*); +extern void leveldb_options_set_block_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_block_restart_interval(leveldb_options_t*, int); + +enum { + leveldb_no_compression = 0, + leveldb_snappy_compression = 1 +}; +extern void leveldb_options_set_compression(leveldb_options_t*, int); + +/* Comparator */ + +extern leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)); +extern void leveldb_comparator_destroy(leveldb_comparator_t*); + +/* Filter policy */ + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)); +extern void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( + int bits_per_key); + +/* Read options */ + +extern leveldb_readoptions_t* leveldb_readoptions_create(); +extern void leveldb_readoptions_destroy(leveldb_readoptions_t*); +extern void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t*, + unsigned char); +extern void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t*, unsigned char); +extern void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t*, + const leveldb_snapshot_t*); + +/* Write options */ + +extern leveldb_writeoptions_t* leveldb_writeoptions_create(); +extern void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); +extern void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t*, unsigned char); + +/* Cache */ + +extern leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); +extern void leveldb_cache_destroy(leveldb_cache_t* cache); + +/* Env */ + +extern leveldb_env_t* leveldb_create_default_env(); +extern void leveldb_env_destroy(leveldb_env_t*); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ diff --git a/src/leveldb/include/leveldb/cache.h b/src/leveldb/include/leveldb/cache.h new file mode 100644 index 0000000000..5e3b47637d --- /dev/null +++ b/src/leveldb/include/leveldb/cache.h @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Cache is an interface that maps keys to values. It has internal +// synchronization and may be safely accessed concurrently from +// multiple threads. It may automatically evict entries to make room +// for new entries. Values have a specified charge against the cache +// capacity. For example, a cache where the values are variable +// length strings, may use the length of the string as the charge for +// the string. +// +// A builtin cache implementation with a least-recently-used eviction +// policy is provided. Clients may use their own implementations if +// they want something more sophisticated (like scan-resistance, a +// custom eviction policy, variable cache sizing, etc.) + +#ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_ +#define STORAGE_LEVELDB_INCLUDE_CACHE_H_ + +#include <stdint.h> +#include "leveldb/slice.h" + +namespace leveldb { + +class Cache; + +// Create a new cache with a fixed size capacity. This implementation +// of Cache uses a least-recently-used eviction policy. +extern Cache* NewLRUCache(size_t capacity); + +class Cache { + public: + Cache() { } + + // Destroys all existing entries by calling the "deleter" + // function that was passed to the constructor. + virtual ~Cache(); + + // Opaque handle to an entry stored in the cache. + struct Handle { }; + + // Insert a mapping from key->value into the cache and assign it + // the specified charge against the total cache capacity. + // + // Returns a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + // + // When the inserted entry is no longer needed, the key and + // value will be passed to "deleter". + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) = 0; + + // If the cache has no mapping for "key", returns NULL. + // + // Else return a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + virtual Handle* Lookup(const Slice& key) = 0; + + // Release a mapping returned by a previous Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void Release(Handle* handle) = 0; + + // Return the value encapsulated in a handle returned by a + // successful Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void* Value(Handle* handle) = 0; + + // If the cache contains entry for key, erase it. Note that the + // underlying entry will be kept around until all existing handles + // to it have been released. + virtual void Erase(const Slice& key) = 0; + + // Return a new numeric id. May be used by multiple clients who are + // sharing the same cache to partition the key space. Typically the + // client will allocate a new id at startup and prepend the id to + // its cache keys. + virtual uint64_t NewId() = 0; + + private: + void LRU_Remove(Handle* e); + void LRU_Append(Handle* e); + void Unref(Handle* e); + + struct Rep; + Rep* rep_; + + // No copying allowed + Cache(const Cache&); + void operator=(const Cache&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CACHE_H_ diff --git a/src/leveldb/include/leveldb/comparator.h b/src/leveldb/include/leveldb/comparator.h new file mode 100644 index 0000000000..556b984c76 --- /dev/null +++ b/src/leveldb/include/leveldb/comparator.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ + +#include <string> + +namespace leveldb { + +class Slice; + +// A Comparator object provides a total order across slices that are +// used as keys in an sstable or a database. A Comparator implementation +// must be thread-safe since leveldb may invoke its methods concurrently +// from multiple threads. +class Comparator { + public: + virtual ~Comparator(); + + // Three-way comparison. Returns value: + // < 0 iff "a" < "b", + // == 0 iff "a" == "b", + // > 0 iff "a" > "b" + virtual int Compare(const Slice& a, const Slice& b) const = 0; + + // The name of the comparator. Used to check for comparator + // mismatches (i.e., a DB created with one comparator is + // accessed using a different comparator. + // + // The client of this package should switch to a new name whenever + // the comparator implementation changes in a way that will cause + // the relative ordering of any two keys to change. + // + // Names starting with "leveldb." are reserved and should not be used + // by any clients of this package. + virtual const char* Name() const = 0; + + // Advanced functions: these are used to reduce the space requirements + // for internal data structures like index blocks. + + // If *start < limit, changes *start to a short string in [start,limit). + // Simple comparator implementations may return with *start unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const = 0; + + // Changes *key to a short string >= *key. + // Simple comparator implementations may return with *key unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortSuccessor(std::string* key) const = 0; +}; + +// Return a builtin comparator that uses lexicographic byte-wise +// ordering. The result remains the property of this module and +// must not be deleted. +extern const Comparator* BytewiseComparator(); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h new file mode 100644 index 0000000000..ed56b87c38 --- /dev/null +++ b/src/leveldb/include/leveldb/db.h @@ -0,0 +1,161 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_DB_H_ +#define STORAGE_LEVELDB_INCLUDE_DB_H_ + +#include <stdint.h> +#include <stdio.h> +#include "leveldb/iterator.h" +#include "leveldb/options.h" + +namespace leveldb { + +// Update Makefile if you change these +static const int kMajorVersion = 1; +static const int kMinorVersion = 5; + +struct Options; +struct ReadOptions; +struct WriteOptions; +class WriteBatch; + +// Abstract handle to particular state of a DB. +// A Snapshot is an immutable object and can therefore be safely +// accessed from multiple threads without any external synchronization. +class Snapshot { + protected: + virtual ~Snapshot(); +}; + +// A range of keys +struct Range { + Slice start; // Included in the range + Slice limit; // Not included in the range + + Range() { } + Range(const Slice& s, const Slice& l) : start(s), limit(l) { } +}; + +// A DB is a persistent ordered map from keys to values. +// A DB is safe for concurrent access from multiple threads without +// any external synchronization. +class DB { + public: + // Open the database with the specified "name". + // Stores a pointer to a heap-allocated database in *dbptr and returns + // OK on success. + // Stores NULL in *dbptr and returns a non-OK status on error. + // Caller should delete *dbptr when it is no longer needed. + static Status Open(const Options& options, + const std::string& name, + DB** dbptr); + + DB() { } + virtual ~DB(); + + // Set the database entry for "key" to "value". Returns OK on success, + // and a non-OK status on error. + // Note: consider setting options.sync = true. + virtual Status Put(const WriteOptions& options, + const Slice& key, + const Slice& value) = 0; + + // Remove the database entry (if any) for "key". Returns OK on + // success, and a non-OK status on error. It is not an error if "key" + // did not exist in the database. + // Note: consider setting options.sync = true. + virtual Status Delete(const WriteOptions& options, const Slice& key) = 0; + + // Apply the specified updates to the database. + // Returns OK on success, non-OK on failure. + // Note: consider setting options.sync = true. + virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0; + + // If the database contains an entry for "key" store the + // corresponding value in *value and return OK. + // + // If there is no entry for "key" leave *value unchanged and return + // a status for which Status::IsNotFound() returns true. + // + // May return some other Status on an error. + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) = 0; + + // Return a heap-allocated iterator over the contents of the database. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + // + // Caller should delete the iterator when it is no longer needed. + // The returned iterator should be deleted before this db is deleted. + virtual Iterator* NewIterator(const ReadOptions& options) = 0; + + // Return a handle to the current DB state. Iterators created with + // this handle will all observe a stable snapshot of the current DB + // state. The caller must call ReleaseSnapshot(result) when the + // snapshot is no longer needed. + virtual const Snapshot* GetSnapshot() = 0; + + // Release a previously acquired snapshot. The caller must not + // use "snapshot" after this call. + virtual void ReleaseSnapshot(const Snapshot* snapshot) = 0; + + // DB implementations can export properties about their state + // via this method. If "property" is a valid property understood by this + // DB implementation, fills "*value" with its current value and returns + // true. Otherwise returns false. + // + // + // Valid property names include: + // + // "leveldb.num-files-at-level<N>" - return the number of files at level <N>, + // where <N> is an ASCII representation of a level number (e.g. "0"). + // "leveldb.stats" - returns a multi-line string that describes statistics + // about the internal operation of the DB. + // "leveldb.sstables" - returns a multi-line string that describes all + // of the sstables that make up the db contents. + virtual bool GetProperty(const Slice& property, std::string* value) = 0; + + // For each i in [0,n-1], store in "sizes[i]", the approximate + // file system space used by keys in "[range[i].start .. range[i].limit)". + // + // Note that the returned sizes measure file system space usage, so + // if the user data compresses by a factor of ten, the returned + // sizes will be one-tenth the size of the corresponding user data size. + // + // The results may not include the sizes of recently written data. + virtual void GetApproximateSizes(const Range* range, int n, + uint64_t* sizes) = 0; + + // Compact the underlying storage for the key range [*begin,*end]. + // In particular, deleted and overwritten versions are discarded, + // and the data is rearranged to reduce the cost of operations + // needed to access the data. This operation should typically only + // be invoked by users who understand the underlying implementation. + // + // begin==NULL is treated as a key before all keys in the database. + // end==NULL is treated as a key after all keys in the database. + // Therefore the following call will compact the entire database: + // db->CompactRange(NULL, NULL); + virtual void CompactRange(const Slice* begin, const Slice* end) = 0; + + private: + // No copying allowed + DB(const DB&); + void operator=(const DB&); +}; + +// Destroy the contents of the specified database. +// Be very careful using this method. +Status DestroyDB(const std::string& name, const Options& options); + +// If a DB cannot be opened, you may attempt to call this method to +// resurrect as much of the contents of the database as possible. +// Some data may be lost, so be careful when calling this function +// on a database that contains important information. +Status RepairDB(const std::string& dbname, const Options& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_DB_H_ diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h new file mode 100644 index 0000000000..2720667185 --- /dev/null +++ b/src/leveldb/include/leveldb/env.h @@ -0,0 +1,323 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An Env is an interface used by the leveldb implementation to access +// operating system functionality like the filesystem etc. Callers +// may wish to provide a custom Env object when opening a database to +// get fine gain control; e.g., to rate limit file system operations. +// +// All Env implementations are safe for concurrent access from +// multiple threads without any external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ +#define STORAGE_LEVELDB_INCLUDE_ENV_H_ + +#include <cstdarg> +#include <string> +#include <vector> +#include <stdint.h> +#include "leveldb/status.h" + +namespace leveldb { + +class FileLock; +class Logger; +class RandomAccessFile; +class SequentialFile; +class Slice; +class WritableFile; + +class Env { + public: + Env() { } + virtual ~Env(); + + // Return a default environment suitable for the current operating + // system. Sophisticated users may wish to provide their own Env + // implementation instead of relying on this default environment. + // + // The result of Default() belongs to leveldb and must never be deleted. + static Env* Default(); + + // Create a brand new sequentially-readable file with the specified name. + // On success, stores a pointer to the new file in *result and returns OK. + // On failure stores NULL in *result and returns non-OK. If the file does + // not exist, returns a non-OK status. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) = 0; + + // Create a brand new random access read-only file with the + // specified name. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. If the file does not exist, returns a non-OK + // status. + // + // The returned file may be concurrently accessed by multiple threads. + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) = 0; + + // Create an object that writes to a new file with the specified + // name. Deletes any existing file with the same name and creates a + // new file. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) = 0; + + // Returns true iff the named file exists. + virtual bool FileExists(const std::string& fname) = 0; + + // Store in *result the names of the children of the specified directory. + // The names are relative to "dir". + // Original contents of *results are dropped. + virtual Status GetChildren(const std::string& dir, + std::vector<std::string>* result) = 0; + + // Delete the named file. + virtual Status DeleteFile(const std::string& fname) = 0; + + // Create the specified directory. + virtual Status CreateDir(const std::string& dirname) = 0; + + // Delete the specified directory. + virtual Status DeleteDir(const std::string& dirname) = 0; + + // Store the size of fname in *file_size. + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0; + + // Rename file src to target. + virtual Status RenameFile(const std::string& src, + const std::string& target) = 0; + + // Lock the specified file. Used to prevent concurrent access to + // the same db by multiple processes. On failure, stores NULL in + // *lock and returns non-OK. + // + // On success, stores a pointer to the object that represents the + // acquired lock in *lock and returns OK. The caller should call + // UnlockFile(*lock) to release the lock. If the process exits, + // the lock will be automatically released. + // + // If somebody else already holds the lock, finishes immediately + // with a failure. I.e., this call does not wait for existing locks + // to go away. + // + // May create the named file if it does not already exist. + virtual Status LockFile(const std::string& fname, FileLock** lock) = 0; + + // Release the lock acquired by a previous successful call to LockFile. + // REQUIRES: lock was returned by a successful LockFile() call + // REQUIRES: lock has not already been unlocked. + virtual Status UnlockFile(FileLock* lock) = 0; + + // Arrange to run "(*function)(arg)" once in a background thread. + // + // "function" may run in an unspecified thread. Multiple functions + // added to the same Env may run concurrently in different threads. + // I.e., the caller may not assume that background work items are + // serialized. + virtual void Schedule( + void (*function)(void* arg), + void* arg) = 0; + + // Start a new thread, invoking "function(arg)" within the new thread. + // When "function(arg)" returns, the thread will be destroyed. + virtual void StartThread(void (*function)(void* arg), void* arg) = 0; + + // *path is set to a temporary directory that can be used for testing. It may + // or many not have just been created. The directory may or may not differ + // between runs of the same process, but subsequent calls will return the + // same directory. + virtual Status GetTestDirectory(std::string* path) = 0; + + // Create and return a log file for storing informational messages. + virtual Status NewLogger(const std::string& fname, Logger** result) = 0; + + // Returns the number of micro-seconds since some fixed point in time. Only + // useful for computing deltas of time. + virtual uint64_t NowMicros() = 0; + + // Sleep/delay the thread for the perscribed number of micro-seconds. + virtual void SleepForMicroseconds(int micros) = 0; + + private: + // No copying allowed + Env(const Env&); + void operator=(const Env&); +}; + +// A file abstraction for reading sequentially through a file +class SequentialFile { + public: + SequentialFile() { } + virtual ~SequentialFile(); + + // Read up to "n" bytes from the file. "scratch[0..n-1]" may be + // written by this routine. Sets "*result" to the data that was + // read (including if fewer than "n" bytes were successfully read). + // May set "*result" to point at data in "scratch[0..n-1]", so + // "scratch[0..n-1]" must be live when "*result" is used. + // If an error was encountered, returns a non-OK status. + // + // REQUIRES: External synchronization + virtual Status Read(size_t n, Slice* result, char* scratch) = 0; + + // Skip "n" bytes from the file. This is guaranteed to be no + // slower that reading the same data, but may be faster. + // + // If end of file is reached, skipping will stop at the end of the + // file, and Skip will return OK. + // + // REQUIRES: External synchronization + virtual Status Skip(uint64_t n) = 0; +}; + +// A file abstraction for randomly reading the contents of a file. +class RandomAccessFile { + public: + RandomAccessFile() { } + virtual ~RandomAccessFile(); + + // Read up to "n" bytes from the file starting at "offset". + // "scratch[0..n-1]" may be written by this routine. Sets "*result" + // to the data that was read (including if fewer than "n" bytes were + // successfully read). May set "*result" to point at data in + // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when + // "*result" is used. If an error was encountered, returns a non-OK + // status. + // + // Safe for concurrent use by multiple threads. + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const = 0; +}; + +// A file abstraction for sequential writing. The implementation +// must provide buffering since callers may append small fragments +// at a time to the file. +class WritableFile { + public: + WritableFile() { } + virtual ~WritableFile(); + + virtual Status Append(const Slice& data) = 0; + virtual Status Close() = 0; + virtual Status Flush() = 0; + virtual Status Sync() = 0; + + private: + // No copying allowed + WritableFile(const WritableFile&); + void operator=(const WritableFile&); +}; + +// An interface for writing log messages. +class Logger { + public: + Logger() { } + virtual ~Logger(); + + // Write an entry to the log file with the specified format. + virtual void Logv(const char* format, va_list ap) = 0; + + private: + // No copying allowed + Logger(const Logger&); + void operator=(const Logger&); +}; + + +// Identifies a locked file. +class FileLock { + public: + FileLock() { } + virtual ~FileLock(); + private: + // No copying allowed + FileLock(const FileLock&); + void operator=(const FileLock&); +}; + +// Log the specified data to *info_log if info_log is non-NULL. +extern void Log(Logger* info_log, const char* format, ...) +# if defined(__GNUC__) || defined(__clang__) + __attribute__((__format__ (__printf__, 2, 3))) +# endif + ; + +// A utility routine: write "data" to the named file. +extern Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname); + +// A utility routine: read contents of named file into *data +extern Status ReadFileToString(Env* env, const std::string& fname, + std::string* data); + +// An implementation of Env that forwards all calls to another Env. +// May be useful to clients who wish to override just part of the +// functionality of another Env. +class EnvWrapper : public Env { + public: + // Initialize an EnvWrapper that delegates all calls to *t + explicit EnvWrapper(Env* t) : target_(t) { } + virtual ~EnvWrapper(); + + // Return the target to which this Env forwards all calls + Env* target() const { return target_; } + + // The following text is boilerplate that forwards all methods to target() + Status NewSequentialFile(const std::string& f, SequentialFile** r) { + return target_->NewSequentialFile(f, r); + } + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + return target_->NewRandomAccessFile(f, r); + } + Status NewWritableFile(const std::string& f, WritableFile** r) { + return target_->NewWritableFile(f, r); + } + bool FileExists(const std::string& f) { return target_->FileExists(f); } + Status GetChildren(const std::string& dir, std::vector<std::string>* r) { + return target_->GetChildren(dir, r); + } + Status DeleteFile(const std::string& f) { return target_->DeleteFile(f); } + Status CreateDir(const std::string& d) { return target_->CreateDir(d); } + Status DeleteDir(const std::string& d) { return target_->DeleteDir(d); } + Status GetFileSize(const std::string& f, uint64_t* s) { + return target_->GetFileSize(f, s); + } + Status RenameFile(const std::string& s, const std::string& t) { + return target_->RenameFile(s, t); + } + Status LockFile(const std::string& f, FileLock** l) { + return target_->LockFile(f, l); + } + Status UnlockFile(FileLock* l) { return target_->UnlockFile(l); } + void Schedule(void (*f)(void*), void* a) { + return target_->Schedule(f, a); + } + void StartThread(void (*f)(void*), void* a) { + return target_->StartThread(f, a); + } + virtual Status GetTestDirectory(std::string* path) { + return target_->GetTestDirectory(path); + } + virtual Status NewLogger(const std::string& fname, Logger** result) { + return target_->NewLogger(fname, result); + } + uint64_t NowMicros() { + return target_->NowMicros(); + } + void SleepForMicroseconds(int micros) { + target_->SleepForMicroseconds(micros); + } + private: + Env* target_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ diff --git a/src/leveldb/include/leveldb/filter_policy.h b/src/leveldb/include/leveldb/filter_policy.h new file mode 100644 index 0000000000..1fba08001f --- /dev/null +++ b/src/leveldb/include/leveldb/filter_policy.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A database can be configured with a custom FilterPolicy object. +// This object is responsible for creating a small filter from a set +// of keys. These filters are stored in leveldb and are consulted +// automatically by leveldb to decide whether or not to read some +// information from disk. In many cases, a filter can cut down the +// number of disk seeks form a handful to a single disk seek per +// DB::Get() call. +// +// Most people will want to use the builtin bloom filter support (see +// NewBloomFilterPolicy() below). + +#ifndef STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ +#define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ + +#include <string> + +namespace leveldb { + +class Slice; + +class FilterPolicy { + public: + virtual ~FilterPolicy(); + + // Return the name of this policy. Note that if the filter encoding + // changes in an incompatible way, the name returned by this method + // must be changed. Otherwise, old incompatible filters may be + // passed to methods of this type. + virtual const char* Name() const = 0; + + // keys[0,n-1] contains a list of keys (potentially with duplicates) + // that are ordered according to the user supplied comparator. + // Append a filter that summarizes keys[0,n-1] to *dst. + // + // Warning: do not change the initial contents of *dst. Instead, + // append the newly constructed filter to *dst. + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) + const = 0; + + // "filter" contains the data appended by a preceding call to + // CreateFilter() on this class. This method must return true if + // the key was in the list of keys passed to CreateFilter(). + // This method may return true or false if the key was not on the + // list, but it should aim to return false with a high probability. + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const = 0; +}; + +// Return a new filter policy that uses a bloom filter with approximately +// the specified number of bits per key. A good value for bits_per_key +// is 10, which yields a filter with ~ 1% false positive rate. +// +// Callers must delete the result after any database that is using the +// result has been closed. +// +// Note: if you are using a custom comparator that ignores some parts +// of the keys being compared, you must not use NewBloomFilterPolicy() +// and must provide your own FilterPolicy that also ignores the +// corresponding parts of the keys. For example, if the comparator +// ignores trailing spaces, it would be incorrect to use a +// FilterPolicy (like NewBloomFilterPolicy) that does not ignore +// trailing spaces in keys. +extern const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); + +} + +#endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ diff --git a/src/leveldb/include/leveldb/iterator.h b/src/leveldb/include/leveldb/iterator.h new file mode 100644 index 0000000000..ad543eb46c --- /dev/null +++ b/src/leveldb/include/leveldb/iterator.h @@ -0,0 +1,100 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An iterator yields a sequence of key/value pairs from a source. +// The following class defines the interface. Multiple implementations +// are provided by this library. In particular, iterators are provided +// to access the contents of a Table or a DB. +// +// Multiple threads can invoke const methods on an Iterator without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Iterator must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ + +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class Iterator { + public: + Iterator(); + virtual ~Iterator(); + + // An iterator is either positioned at a key/value pair, or + // not valid. This method returns true iff the iterator is valid. + virtual bool Valid() const = 0; + + // Position at the first key in the source. The iterator is Valid() + // after this call iff the source is not empty. + virtual void SeekToFirst() = 0; + + // Position at the last key in the source. The iterator is + // Valid() after this call iff the source is not empty. + virtual void SeekToLast() = 0; + + // Position at the first key in the source that at or past target + // The iterator is Valid() after this call iff the source contains + // an entry that comes at or past target. + virtual void Seek(const Slice& target) = 0; + + // Moves to the next entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the last entry in the source. + // REQUIRES: Valid() + virtual void Next() = 0; + + // Moves to the previous entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the first entry in source. + // REQUIRES: Valid() + virtual void Prev() = 0; + + // Return the key for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: Valid() + virtual Slice key() const = 0; + + // Return the value for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: !AtEnd() && !AtStart() + virtual Slice value() const = 0; + + // If an error has occurred, return it. Else return an ok status. + virtual Status status() const = 0; + + // Clients are allowed to register function/arg1/arg2 triples that + // will be invoked when this iterator is destroyed. + // + // Note that unlike all of the preceding methods, this method is + // not abstract and therefore clients should not override it. + typedef void (*CleanupFunction)(void* arg1, void* arg2); + void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2); + + private: + struct Cleanup { + CleanupFunction function; + void* arg1; + void* arg2; + Cleanup* next; + }; + Cleanup cleanup_; + + // No copying allowed + Iterator(const Iterator&); + void operator=(const Iterator&); +}; + +// Return an empty iterator (yields nothing). +extern Iterator* NewEmptyIterator(); + +// Return an empty iterator with the specified status. +extern Iterator* NewErrorIterator(const Status& status); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ diff --git a/src/leveldb/include/leveldb/options.h b/src/leveldb/include/leveldb/options.h new file mode 100644 index 0000000000..fdda718d30 --- /dev/null +++ b/src/leveldb/include/leveldb/options.h @@ -0,0 +1,195 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ +#define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ + +#include <stddef.h> + +namespace leveldb { + +class Cache; +class Comparator; +class Env; +class FilterPolicy; +class Logger; +class Snapshot; + +// DB contents are stored in a set of blocks, each of which holds a +// sequence of key,value pairs. Each block may be compressed before +// being stored in a file. The following enum describes which +// compression method (if any) is used to compress a block. +enum CompressionType { + // NOTE: do not change the values of existing entries, as these are + // part of the persistent format on disk. + kNoCompression = 0x0, + kSnappyCompression = 0x1 +}; + +// Options to control the behavior of a database (passed to DB::Open) +struct Options { + // ------------------- + // Parameters that affect behavior + + // Comparator used to define the order of keys in the table. + // Default: a comparator that uses lexicographic byte-wise ordering + // + // REQUIRES: The client must ensure that the comparator supplied + // here has the same name and orders keys *exactly* the same as the + // comparator provided to previous open calls on the same DB. + const Comparator* comparator; + + // If true, the database will be created if it is missing. + // Default: false + bool create_if_missing; + + // If true, an error is raised if the database already exists. + // Default: false + bool error_if_exists; + + // If true, the implementation will do aggressive checking of the + // data it is processing and will stop early if it detects any + // errors. This may have unforeseen ramifications: for example, a + // corruption of one DB entry may cause a large number of entries to + // become unreadable or for the entire DB to become unopenable. + // Default: false + bool paranoid_checks; + + // Use the specified object to interact with the environment, + // e.g. to read/write files, schedule background work, etc. + // Default: Env::Default() + Env* env; + + // Any internal progress/error information generated by the db will + // be written to info_log if it is non-NULL, or to a file stored + // in the same directory as the DB contents if info_log is NULL. + // Default: NULL + Logger* info_log; + + // ------------------- + // Parameters that affect performance + + // Amount of data to build up in memory (backed by an unsorted log + // on disk) before converting to a sorted on-disk file. + // + // Larger values increase performance, especially during bulk loads. + // Up to two write buffers may be held in memory at the same time, + // so you may wish to adjust this parameter to control memory usage. + // Also, a larger write buffer will result in a longer recovery time + // the next time the database is opened. + // + // Default: 4MB + size_t write_buffer_size; + + // Number of open files that can be used by the DB. You may need to + // increase this if your database has a large working set (budget + // one open file per 2MB of working set). + // + // Default: 1000 + int max_open_files; + + // Control over blocks (user data is stored in a set of blocks, and + // a block is the unit of reading from disk). + + // If non-NULL, use the specified cache for blocks. + // If NULL, leveldb will automatically create and use an 8MB internal cache. + // Default: NULL + Cache* block_cache; + + // Approximate size of user data packed per block. Note that the + // block size specified here corresponds to uncompressed data. The + // actual size of the unit read from disk may be smaller if + // compression is enabled. This parameter can be changed dynamically. + // + // Default: 4K + size_t block_size; + + // Number of keys between restart points for delta encoding of keys. + // This parameter can be changed dynamically. Most clients should + // leave this parameter alone. + // + // Default: 16 + int block_restart_interval; + + // Compress blocks using the specified compression algorithm. This + // parameter can be changed dynamically. + // + // Default: kSnappyCompression, which gives lightweight but fast + // compression. + // + // Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz: + // ~200-500MB/s compression + // ~400-800MB/s decompression + // Note that these speeds are significantly faster than most + // persistent storage speeds, and therefore it is typically never + // worth switching to kNoCompression. Even if the input data is + // incompressible, the kSnappyCompression implementation will + // efficiently detect that and will switch to uncompressed mode. + CompressionType compression; + + // If non-NULL, use the specified filter policy to reduce disk reads. + // Many applications will benefit from passing the result of + // NewBloomFilterPolicy() here. + // + // Default: NULL + const FilterPolicy* filter_policy; + + // Create an Options object with default values for all fields. + Options(); +}; + +// Options that control read operations +struct ReadOptions { + // If true, all data read from underlying storage will be + // verified against corresponding checksums. + // Default: false + bool verify_checksums; + + // Should the data read for this iteration be cached in memory? + // Callers may wish to set this field to false for bulk scans. + // Default: true + bool fill_cache; + + // If "snapshot" is non-NULL, read as of the supplied snapshot + // (which must belong to the DB that is being read and which must + // not have been released). If "snapshot" is NULL, use an impliicit + // snapshot of the state at the beginning of this read operation. + // Default: NULL + const Snapshot* snapshot; + + ReadOptions() + : verify_checksums(false), + fill_cache(true), + snapshot(NULL) { + } +}; + +// Options that control write operations +struct WriteOptions { + // If true, the write will be flushed from the operating system + // buffer cache (by calling WritableFile::Sync()) before the write + // is considered complete. If this flag is true, writes will be + // slower. + // + // If this flag is false, and the machine crashes, some recent + // writes may be lost. Note that if it is just the process that + // crashes (i.e., the machine does not reboot), no writes will be + // lost even if sync==false. + // + // In other words, a DB write with sync==false has similar + // crash semantics as the "write()" system call. A DB write + // with sync==true has similar crash semantics to a "write()" + // system call followed by "fsync()". + // + // Default: false + bool sync; + + WriteOptions() + : sync(false) { + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ diff --git a/src/leveldb/include/leveldb/slice.h b/src/leveldb/include/leveldb/slice.h new file mode 100644 index 0000000000..74ea8fa49a --- /dev/null +++ b/src/leveldb/include/leveldb/slice.h @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Slice is a simple structure containing a pointer into some external +// storage and a size. The user of a Slice must ensure that the slice +// is not used after the corresponding external storage has been +// deallocated. +// +// Multiple threads can invoke const methods on a Slice without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Slice must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_SLICE_H_ +#define STORAGE_LEVELDB_INCLUDE_SLICE_H_ + +#include <assert.h> +#include <stddef.h> +#include <string.h> +#include <string> + +namespace leveldb { + +class Slice { + public: + // Create an empty slice. + Slice() : data_(""), size_(0) { } + + // Create a slice that refers to d[0,n-1]. + Slice(const char* d, size_t n) : data_(d), size_(n) { } + + // Create a slice that refers to the contents of "s" + Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } + + // Create a slice that refers to s[0,strlen(s)-1] + Slice(const char* s) : data_(s), size_(strlen(s)) { } + + // Return a pointer to the beginning of the referenced data + const char* data() const { return data_; } + + // Return the length (in bytes) of the referenced data + size_t size() const { return size_; } + + // Return true iff the length of the referenced data is zero + bool empty() const { return size_ == 0; } + + // Return the ith byte in the referenced data. + // REQUIRES: n < size() + char operator[](size_t n) const { + assert(n < size()); + return data_[n]; + } + + // Change this slice to refer to an empty array + void clear() { data_ = ""; size_ = 0; } + + // Drop the first "n" bytes from this slice. + void remove_prefix(size_t n) { + assert(n <= size()); + data_ += n; + size_ -= n; + } + + // Return a string that contains the copy of the referenced data. + std::string ToString() const { return std::string(data_, size_); } + + // Three-way comparison. Returns value: + // < 0 iff "*this" < "b", + // == 0 iff "*this" == "b", + // > 0 iff "*this" > "b" + int compare(const Slice& b) const; + + // Return true iff "x" is a prefix of "*this" + bool starts_with(const Slice& x) const { + return ((size_ >= x.size_) && + (memcmp(data_, x.data_, x.size_) == 0)); + } + + private: + const char* data_; + size_t size_; + + // Intentionally copyable +}; + +inline bool operator==(const Slice& x, const Slice& y) { + return ((x.size() == y.size()) && + (memcmp(x.data(), y.data(), x.size()) == 0)); +} + +inline bool operator!=(const Slice& x, const Slice& y) { + return !(x == y); +} + +inline int Slice::compare(const Slice& b) const { + const int min_len = (size_ < b.size_) ? size_ : b.size_; + int r = memcmp(data_, b.data_, min_len); + if (r == 0) { + if (size_ < b.size_) r = -1; + else if (size_ > b.size_) r = +1; + } + return r; +} + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ diff --git a/src/leveldb/include/leveldb/status.h b/src/leveldb/include/leveldb/status.h new file mode 100644 index 0000000000..11dbd4b47e --- /dev/null +++ b/src/leveldb/include/leveldb/status.h @@ -0,0 +1,106 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Status encapsulates the result of an operation. It may indicate success, +// or it may indicate an error with an associated error message. +// +// Multiple threads can invoke const methods on a Status without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Status must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_ +#define STORAGE_LEVELDB_INCLUDE_STATUS_H_ + +#include <string> +#include "leveldb/slice.h" + +namespace leveldb { + +class Status { + public: + // Create a success status. + Status() : state_(NULL) { } + ~Status() { delete[] state_; } + + // Copy the specified status. + Status(const Status& s); + void operator=(const Status& s); + + // Return a success status. + static Status OK() { return Status(); } + + // Return error status of an appropriate type. + static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotFound, msg, msg2); + } + static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kCorruption, msg, msg2); + } + static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotSupported, msg, msg2); + } + static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kInvalidArgument, msg, msg2); + } + static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kIOError, msg, msg2); + } + + // Returns true iff the status indicates success. + bool ok() const { return (state_ == NULL); } + + // Returns true iff the status indicates a NotFound error. + bool IsNotFound() const { return code() == kNotFound; } + + // Returns true iff the status indicates a Corruption error. + bool IsCorruption() const { return code() == kCorruption; } + + // Returns true iff the status indicates an IOError. + bool IsIOError() const { return code() == kIOError; } + + // Return a string representation of this status suitable for printing. + // Returns the string "OK" for success. + std::string ToString() const; + + private: + // OK status has a NULL state_. Otherwise, state_ is a new[] array + // of the following form: + // state_[0..3] == length of message + // state_[4] == code + // state_[5..] == message + const char* state_; + + enum Code { + kOk = 0, + kNotFound = 1, + kCorruption = 2, + kNotSupported = 3, + kInvalidArgument = 4, + kIOError = 5 + }; + + Code code() const { + return (state_ == NULL) ? kOk : static_cast<Code>(state_[4]); + } + + Status(Code code, const Slice& msg, const Slice& msg2); + static const char* CopyState(const char* s); +}; + +inline Status::Status(const Status& s) { + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); +} +inline void Status::operator=(const Status& s) { + // The following condition catches both aliasing (when this == &s), + // and the common case where both s and *this are ok. + if (state_ != s.state_) { + delete[] state_; + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); + } +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_STATUS_H_ diff --git a/src/leveldb/include/leveldb/table.h b/src/leveldb/include/leveldb/table.h new file mode 100644 index 0000000000..a9746c3f5e --- /dev/null +++ b/src/leveldb/include/leveldb/table.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_H_ + +#include <stdint.h> +#include "leveldb/iterator.h" + +namespace leveldb { + +class Block; +class BlockHandle; +class Footer; +struct Options; +class RandomAccessFile; +struct ReadOptions; +class TableCache; + +// A Table is a sorted map from strings to strings. Tables are +// immutable and persistent. A Table may be safely accessed from +// multiple threads without external synchronization. +class Table { + public: + // Attempt to open the table that is stored in bytes [0..file_size) + // of "file", and read the metadata entries necessary to allow + // retrieving data from the table. + // + // If successful, returns ok and sets "*table" to the newly opened + // table. The client should delete "*table" when no longer needed. + // If there was an error while initializing the table, sets "*table" + // to NULL and returns a non-ok status. Does not take ownership of + // "*source", but the client must ensure that "source" remains live + // for the duration of the returned table's lifetime. + // + // *file must remain live while this Table is in use. + static Status Open(const Options& options, + RandomAccessFile* file, + uint64_t file_size, + Table** table); + + ~Table(); + + // Returns a new iterator over the table contents. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + Iterator* NewIterator(const ReadOptions&) const; + + // Given a key, return an approximate byte offset in the file where + // the data for that key begins (or would begin if the key were + // present in the file). The returned value is in terms of file + // bytes, and so includes effects like compression of the underlying data. + // E.g., the approximate offset of the last key in the table will + // be close to the file length. + uint64_t ApproximateOffsetOf(const Slice& key) const; + + private: + struct Rep; + Rep* rep_; + + explicit Table(Rep* rep) { rep_ = rep; } + static Iterator* BlockReader(void*, const ReadOptions&, const Slice&); + + // Calls (*handle_result)(arg, ...) with the entry found after a call + // to Seek(key). May not make such a call if filter policy says + // that key is not present. + friend class TableCache; + Status InternalGet( + const ReadOptions&, const Slice& key, + void* arg, + void (*handle_result)(void* arg, const Slice& k, const Slice& v)); + + + void ReadMeta(const Footer& footer); + void ReadFilter(const Slice& filter_handle_value); + + // No copying allowed + Table(const Table&); + void operator=(const Table&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_H_ diff --git a/src/leveldb/include/leveldb/table_builder.h b/src/leveldb/include/leveldb/table_builder.h new file mode 100644 index 0000000000..5fd1dc71f1 --- /dev/null +++ b/src/leveldb/include/leveldb/table_builder.h @@ -0,0 +1,92 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// TableBuilder provides the interface used to build a Table +// (an immutable and sorted map from keys to values). +// +// Multiple threads can invoke const methods on a TableBuilder without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same TableBuilder must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ + +#include <stdint.h> +#include "leveldb/options.h" +#include "leveldb/status.h" + +namespace leveldb { + +class BlockBuilder; +class BlockHandle; +class WritableFile; + +class TableBuilder { + public: + // Create a builder that will store the contents of the table it is + // building in *file. Does not close the file. It is up to the + // caller to close the file after calling Finish(). + TableBuilder(const Options& options, WritableFile* file); + + // REQUIRES: Either Finish() or Abandon() has been called. + ~TableBuilder(); + + // Change the options used by this builder. Note: only some of the + // option fields can be changed after construction. If a field is + // not allowed to change dynamically and its value in the structure + // passed to the constructor is different from its value in the + // structure passed to this method, this method will return an error + // without changing any fields. + Status ChangeOptions(const Options& options); + + // Add key,value to the table being constructed. + // REQUIRES: key is after any previously added key according to comparator. + // REQUIRES: Finish(), Abandon() have not been called + void Add(const Slice& key, const Slice& value); + + // Advanced operation: flush any buffered key/value pairs to file. + // Can be used to ensure that two adjacent entries never live in + // the same data block. Most clients should not need to use this method. + // REQUIRES: Finish(), Abandon() have not been called + void Flush(); + + // Return non-ok iff some error has been detected. + Status status() const; + + // Finish building the table. Stops using the file passed to the + // constructor after this function returns. + // REQUIRES: Finish(), Abandon() have not been called + Status Finish(); + + // Indicate that the contents of this builder should be abandoned. Stops + // using the file passed to the constructor after this function returns. + // If the caller is not going to call Finish(), it must call Abandon() + // before destroying this builder. + // REQUIRES: Finish(), Abandon() have not been called + void Abandon(); + + // Number of calls to Add() so far. + uint64_t NumEntries() const; + + // Size of the file generated so far. If invoked after a successful + // Finish() call, returns the size of the final generated file. + uint64_t FileSize() const; + + private: + bool ok() const { return status().ok(); } + void WriteBlock(BlockBuilder* block, BlockHandle* handle); + void WriteRawBlock(const Slice& data, CompressionType, BlockHandle* handle); + + struct Rep; + Rep* rep_; + + // No copying allowed + TableBuilder(const TableBuilder&); + void operator=(const TableBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ diff --git a/src/leveldb/include/leveldb/write_batch.h b/src/leveldb/include/leveldb/write_batch.h new file mode 100644 index 0000000000..ee9aab68e0 --- /dev/null +++ b/src/leveldb/include/leveldb/write_batch.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch holds a collection of updates to apply atomically to a DB. +// +// The updates are applied in the order in which they are added +// to the WriteBatch. For example, the value of "key" will be "v3" +// after the following batch is written: +// +// batch.Put("key", "v1"); +// batch.Delete("key"); +// batch.Put("key", "v2"); +// batch.Put("key", "v3"); +// +// Multiple threads can invoke const methods on a WriteBatch without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same WriteBatch must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ +#define STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ + +#include <string> +#include "leveldb/status.h" + +namespace leveldb { + +class Slice; + +class WriteBatch { + public: + WriteBatch(); + ~WriteBatch(); + + // Store the mapping "key->value" in the database. + void Put(const Slice& key, const Slice& value); + + // If the database contains a mapping for "key", erase it. Else do nothing. + void Delete(const Slice& key); + + // Clear all updates buffered in this batch. + void Clear(); + + // Support for iterating over the contents of a batch. + class Handler { + public: + virtual ~Handler(); + virtual void Put(const Slice& key, const Slice& value) = 0; + virtual void Delete(const Slice& key) = 0; + }; + Status Iterate(Handler* handler) const; + + private: + friend class WriteBatchInternal; + + std::string rep_; // See comment in write_batch.cc for the format of rep_ + + // Intentionally copyable +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ diff --git a/src/leveldb/port/README b/src/leveldb/port/README new file mode 100644 index 0000000000..422563e25c --- /dev/null +++ b/src/leveldb/port/README @@ -0,0 +1,10 @@ +This directory contains interfaces and implementations that isolate the +rest of the package from platform details. + +Code in the rest of the package includes "port.h" from this directory. +"port.h" in turn includes a platform specific "port_<platform>.h" file +that provides the platform specific implementation. + +See port_posix.h for an example of what must be provided in a platform +specific header file. + diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h new file mode 100644 index 0000000000..c58bffbf1b --- /dev/null +++ b/src/leveldb/port/atomic_pointer.h @@ -0,0 +1,152 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// AtomicPointer provides storage for a lock-free pointer. +// Platform-dependent implementation of AtomicPointer: +// - If the platform provides a cheap barrier, we use it with raw pointers +// - If cstdatomic is present (on newer versions of gcc, it is), we use +// a cstdatomic-based AtomicPointer. However we prefer the memory +// barrier based version, because at least on a gcc 4.4 32-bit build +// on linux, we have encountered a buggy <cstdatomic> +// implementation. Also, some <cstdatomic> implementations are much +// slower than a memory-barrier based implementation (~16ns for +// <cstdatomic> based acquire-load vs. ~1ns for a barrier based +// acquire-load). +// This code is based on atomicops-internals-* in Google's perftools: +// http://code.google.com/p/google-perftools/source/browse/#svn%2Ftrunk%2Fsrc%2Fbase + +#ifndef PORT_ATOMIC_POINTER_H_ +#define PORT_ATOMIC_POINTER_H_ + +#include <stdint.h> +#ifdef LEVELDB_CSTDATOMIC_PRESENT +#include <cstdatomic> +#endif +#ifdef OS_WIN +#include <windows.h> +#endif +#ifdef OS_MACOSX +#include <libkern/OSAtomic.h> +#endif + +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#endif + +namespace leveldb { +namespace port { + +// Define MemoryBarrier() if available +// Windows on x86 +#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY) +// windows.h already provides a MemoryBarrier(void) macro +// http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Gcc on x86 +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + __asm__ __volatile__("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Sun Studio +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + asm volatile("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Mac OS +#elif defined(OS_MACOSX) +inline void MemoryBarrier() { + OSMemoryBarrier(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// ARM Linux +#elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__) +typedef void (*LinuxKernelMemoryBarrierFunc)(void); +// The Linux ARM kernel provides a highly optimized device-specific memory +// barrier function at a fixed memory address that is mapped in every +// user-level process. +// +// This beats using CPU-specific instructions which are, on single-core +// devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more +// than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking +// shows that the extra function call cost is completely negligible on +// multi-core devices. +// +inline void MemoryBarrier() { + (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +#endif + +// AtomicPointer built using platform-specific MemoryBarrier() +#if defined(LEVELDB_HAVE_MEMORY_BARRIER) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* p) : rep_(p) {} + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } + inline void* Acquire_Load() const { + void* result = rep_; + MemoryBarrier(); + return result; + } + inline void Release_Store(void* v) { + MemoryBarrier(); + rep_ = v; + } +}; + +// AtomicPointer based on <cstdatomic> +#elif defined(LEVELDB_CSTDATOMIC_PRESENT) +class AtomicPointer { + private: + std::atomic<void*> rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + return rep_.load(std::memory_order_acquire); + } + inline void Release_Store(void* v) { + rep_.store(v, std::memory_order_release); + } + inline void* NoBarrier_Load() const { + return rep_.load(std::memory_order_relaxed); + } + inline void NoBarrier_Store(void* v) { + rep_.store(v, std::memory_order_relaxed); + } +}; + +// We have neither MemoryBarrier(), nor <cstdatomic> +#else +#error Please implement AtomicPointer for this platform. + +#endif + +#undef LEVELDB_HAVE_MEMORY_BARRIER +#undef ARCH_CPU_X86_FAMILY +#undef ARCH_CPU_ARM_FAMILY + +} // namespace port +} // namespace leveldb + +#endif // PORT_ATOMIC_POINTER_H_ diff --git a/src/leveldb/port/port.h b/src/leveldb/port/port.h new file mode 100644 index 0000000000..4baafa8e22 --- /dev/null +++ b/src/leveldb/port/port.h @@ -0,0 +1,21 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_PORT_H_ +#define STORAGE_LEVELDB_PORT_PORT_H_ + +#include <string.h> + +// Include the appropriate platform specific file below. If you are +// porting to a new platform, see "port_example.h" for documentation +// of what the new port_<platform>.h file must provide. +#if defined(LEVELDB_PLATFORM_POSIX) +# include "port/port_posix.h" +#elif defined(LEVELDB_PLATFORM_CHROMIUM) +# include "port/port_chromium.h" +#elif defined(LEVELDB_PLATFORM_WINDOWS) +# include "port/port_win.h" +#endif + +#endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/src/leveldb/port/port_example.h b/src/leveldb/port/port_example.h new file mode 100644 index 0000000000..ab9e489b32 --- /dev/null +++ b/src/leveldb/port/port_example.h @@ -0,0 +1,135 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// This file contains the specification, but not the implementations, +// of the types/operations/etc. that should be defined by a platform +// specific port_<platform>.h file. Use this file as a reference for +// how to port this package to a new platform. + +#ifndef STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ +#define STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ + +namespace leveldb { +namespace port { + +// TODO(jorlow): Many of these belong more in the environment class rather than +// here. We should try moving them and see if it affects perf. + +// The following boolean constant must be true on a little-endian machine +// and false otherwise. +static const bool kLittleEndian = true /* or some other expression */; + +// ------------------ Threading ------------------- + +// A Mutex represents an exclusive lock. +class Mutex { + public: + Mutex(); + ~Mutex(); + + // Lock the mutex. Waits until other lockers have exited. + // Will deadlock if the mutex is already locked by this thread. + void Lock(); + + // Unlock the mutex. + // REQUIRES: This mutex was locked by this thread. + void Unlock(); + + // Optionally crash if this thread does not hold this mutex. + // The implementation must be fast, especially if NDEBUG is + // defined. The implementation is allowed to skip all checks. + void AssertHeld(); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + + // Atomically release *mu and block on this condition variable until + // either a call to SignalAll(), or a call to Signal() that picks + // this thread to wakeup. + // REQUIRES: this thread holds *mu + void Wait(); + + // If there are some threads waiting, wake up at least one of them. + void Signal(); + + // Wake up all waiting threads. + void SignallAll(); +}; + +// Thread-safe initialization. +// Used as follows: +// static port::OnceType init_control = LEVELDB_ONCE_INIT; +// static void Initializer() { ... do something ...; } +// ... +// port::InitOnce(&init_control, &Initializer); +typedef intptr_t OnceType; +#define LEVELDB_ONCE_INIT 0 +extern void InitOnce(port::OnceType*, void (*initializer)()); + +// A type that holds a pointer that can be read or written atomically +// (i.e., without word-tearing.) +class AtomicPointer { + private: + intptr_t rep_; + public: + // Initialize to arbitrary value + AtomicPointer(); + + // Initialize to hold v + explicit AtomicPointer(void* v) : rep_(v) { } + + // Read and return the stored pointer with the guarantee that no + // later memory access (read or write) by this thread can be + // reordered ahead of this read. + void* Acquire_Load() const; + + // Set v as the stored pointer with the guarantee that no earlier + // memory access (read or write) by this thread can be reordered + // after this store. + void Release_Store(void* v); + + // Read the stored pointer with no ordering guarantees. + void* NoBarrier_Load() const; + + // Set va as the stored pointer with no ordering guarantees. + void NoBarrier_Store(void* v); +}; + +// ------------------ Compression ------------------- + +// Store the snappy compression of "input[0,input_length-1]" in *output. +// Returns false if snappy is not supported by this port. +extern bool Snappy_Compress(const char* input, size_t input_length, + std::string* output); + +// If input[0,input_length-1] looks like a valid snappy compressed +// buffer, store the size of the uncompressed data in *result and +// return true. Else return false. +extern bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result); + +// Attempt to snappy uncompress input[0,input_length-1] into *output. +// Returns true if successful, false if the input is invalid lightweight +// compressed data. +// +// REQUIRES: at least the first "n" bytes of output[] must be writable +// where "n" is the result of a successful call to +// Snappy_GetUncompressedLength. +extern bool Snappy_Uncompress(const char* input_data, size_t input_length, + char* output); + +// ------------------ Miscellaneous ------------------- + +// If heap profiling is not supported, returns false. +// Else repeatedly calls (*func)(arg, data, n) and then returns true. +// The concatenation of all "data[0,n-1]" fragments is the heap profile. +extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ diff --git a/src/leveldb/port/port_posix.cc b/src/leveldb/port/port_posix.cc new file mode 100644 index 0000000000..5ba127a5b9 --- /dev/null +++ b/src/leveldb/port/port_posix.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "port/port_posix.h" + +#include <cstdlib> +#include <stdio.h> +#include <string.h> +#include "util/logging.h" + +namespace leveldb { +namespace port { + +static void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + abort(); + } +} + +Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); } + +Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } + +void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); } + +void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } + +CondVar::CondVar(Mutex* mu) + : mu_(mu) { + PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); +} + +CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } + +void CondVar::Wait() { + PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); +} + +void CondVar::Signal() { + PthreadCall("signal", pthread_cond_signal(&cv_)); +} + +void CondVar::SignalAll() { + PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); +} + +void InitOnce(OnceType* once, void (*initializer)()) { + PthreadCall("once", pthread_once(once, initializer)); +} + +} // namespace port +} // namespace leveldb diff --git a/src/leveldb/port/port_posix.h b/src/leveldb/port/port_posix.h new file mode 100644 index 0000000000..654a4b9d3d --- /dev/null +++ b/src/leveldb/port/port_posix.h @@ -0,0 +1,144 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +#ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_ +#define STORAGE_LEVELDB_PORT_PORT_POSIX_H_ + +#undef PLATFORM_IS_LITTLE_ENDIAN +#if defined(OS_MACOSX) + #include <machine/endian.h> + #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) + #define PLATFORM_IS_LITTLE_ENDIAN \ + (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) + #endif +#elif defined(OS_SOLARIS) + #include <sys/isa_defs.h> + #ifdef _LITTLE_ENDIAN + #define PLATFORM_IS_LITTLE_ENDIAN true + #else + #define PLATFORM_IS_LITTLE_ENDIAN false + #endif +#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\ + defined(OS_DRAGONFLYBSD) || defined(OS_ANDROID) + #include <sys/types.h> + #include <sys/endian.h> +#else + #include <endian.h> +#endif +#include <pthread.h> +#ifdef SNAPPY +#include <snappy.h> +#endif +#include <stdint.h> +#include <string> +#include "port/atomic_pointer.h" + +#ifndef PLATFORM_IS_LITTLE_ENDIAN +#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) +#endif + +#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ + defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ + defined(OS_ANDROID) +// Use fread/fwrite/fflush on platforms without _unlocked variants +#define fread_unlocked fread +#define fwrite_unlocked fwrite +#define fflush_unlocked fflush +#endif + +#if defined(OS_MACOSX) || defined(OS_FREEBSD) ||\ + defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) +// Use fsync() on platforms without fdatasync() +#define fdatasync fsync +#endif + +#if defined(OS_ANDROID) && __ANDROID_API__ < 9 +// fdatasync() was only introduced in API level 9 on Android. Use fsync() +// when targetting older platforms. +#define fdatasync fsync +#endif + +namespace leveldb { +namespace port { + +static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; +#undef PLATFORM_IS_LITTLE_ENDIAN + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld() { } + + private: + friend class CondVar; + pthread_mutex_t mu_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + pthread_cond_t cv_; + Mutex* mu_; +}; + +typedef pthread_once_t OnceType; +#define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT +extern void InitOnce(OnceType* once, void (*initializer)()); + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ diff --git a/src/leveldb/port/port_win.cc b/src/leveldb/port/port_win.cc new file mode 100644 index 0000000000..786cd6018a --- /dev/null +++ b/src/leveldb/port/port_win.cc @@ -0,0 +1,182 @@ +// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of California, Berkeley nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "port/port_win.h" + +#include <windows.h> +#include <cassert> + +namespace leveldb { +namespace port { + +Mutex::Mutex() : + mutex_(::CreateMutex(NULL, FALSE, NULL)) { + assert(mutex_); +} + +Mutex::~Mutex() { + assert(mutex_); + ::CloseHandle(mutex_); +} + +void Mutex::Lock() { + assert(mutex_); + ::WaitForSingleObject(mutex_, INFINITE); +} + +void Mutex::Unlock() { + assert(mutex_); + ::ReleaseMutex(mutex_); +} + +void Mutex::AssertHeld() { + assert(mutex_); + assert(1); +} + +CondVar::CondVar(Mutex* mu) : + waiting_(0), + mu_(mu), + sema_(::CreateSemaphore(NULL, 0, 0x7fffffff, NULL)), + event_(::CreateEvent(NULL, FALSE, FALSE, NULL)), + broadcasted_(false){ + assert(mu_); +} + +CondVar::~CondVar() { + ::CloseHandle(sema_); + ::CloseHandle(event_); +} + +void CondVar::Wait() { + wait_mtx_.Lock(); + ++waiting_; + assert(waiting_ > 0); + wait_mtx_.Unlock(); + + ::SignalObjectAndWait(mu_->mutex_, sema_, INFINITE, FALSE); + + wait_mtx_.Lock(); + bool last = broadcasted_ && (--waiting_ == 0); + assert(waiting_ >= 0); + wait_mtx_.Unlock(); + + // we leave this function with the mutex held + if (last) + { + ::SignalObjectAndWait(event_, mu_->mutex_, INFINITE, FALSE); + } + else + { + ::WaitForSingleObject(mu_->mutex_, INFINITE); + } +} + +void CondVar::Signal() { + wait_mtx_.Lock(); + bool waiters = waiting_ > 0; + wait_mtx_.Unlock(); + + if (waiters) + { + ::ReleaseSemaphore(sema_, 1, 0); + } +} + +void CondVar::SignalAll() { + wait_mtx_.Lock(); + + broadcasted_ = (waiting_ > 0); + + if (broadcasted_) + { + // release all + ::ReleaseSemaphore(sema_, waiting_, 0); + wait_mtx_.Unlock(); + ::WaitForSingleObject(event_, INFINITE); + broadcasted_ = false; + } + else + { + wait_mtx_.Unlock(); + } +} + +AtomicPointer::AtomicPointer(void* v) { + Release_Store(v); +} + +void* AtomicPointer::Acquire_Load() const { + void * p = NULL; + InterlockedExchangePointer(&p, rep_); + return p; +} + +void AtomicPointer::Release_Store(void* v) { + InterlockedExchangePointer(&rep_, v); +} + +void* AtomicPointer::NoBarrier_Load() const { + return rep_; +} + +void AtomicPointer::NoBarrier_Store(void* v) { + rep_ = v; +} + +enum InitializationState +{ + Uninitialized = 0, + Running = 1, + Initialized = 2 +}; + +void InitOnce(OnceType* once, void (*initializer)()) { + + assert(Uninitialized == LEVELDB_ONCE_INIT); + + InitializationState state = static_cast<InitializationState>(InterlockedCompareExchange(once, Running, Uninitialized)); + + if (state == Uninitialized) { + initializer(); + *once = Initialized; + } + + if (state == Running) { + while(*once != Initialized) { + Sleep(0); // yield + } + } + + assert(*once == Initialized); +} + +} +} diff --git a/src/leveldb/port/port_win.h b/src/leveldb/port/port_win.h new file mode 100644 index 0000000000..893919998c --- /dev/null +++ b/src/leveldb/port/port_win.h @@ -0,0 +1,161 @@ +// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of California, Berkeley nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef STORAGE_LEVELDB_PORT_PORT_WIN_H_ +#define STORAGE_LEVELDB_PORT_PORT_WIN_H_ + +#ifdef _MSC_VER +#define snprintf _snprintf +#define close _close +#define fread_unlocked _fread_nolock +#endif + + +#ifdef SNAPPY +#include <snappy/snappy.h> +#endif + +#include <string> + +#include <stdint.h> + +namespace leveldb { +namespace port { + +// Windows is little endian (for now :p) +static const bool kLittleEndian = true; + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld(); + + private: + friend class CondVar; + // critical sections are more efficient than mutexes + // but they are not recursive and can only be used to synchronize threads within the same process + // additionnaly they cannot be used with SignalObjectAndWait that we use for CondVar + // we use opaque void * to avoid including windows.h in port_win.h + void * mutex_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +// the Win32 API offers a dependable condition variable mechanism, but only starting with +// Windows 2008 and Vista +// no matter what we will implement our own condition variable with a semaphore +// implementation as described in a paper written by Douglas C. Schmidt and Irfan Pyarali +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + Mutex* mu_; + + Mutex wait_mtx_; + long waiting_; + + void * sema_; + void * event_; + + bool broadcasted_; +}; + +// Storage for a lock-free pointer +class AtomicPointer { + private: + void * rep_; + public: + AtomicPointer() : rep_(NULL) { } + explicit AtomicPointer(void* v); + void* Acquire_Load() const; + + void Release_Store(void* v); + + void* NoBarrier_Load() const; + + void NoBarrier_Store(void* v); +}; + +typedef volatile long OnceType; +#define LEVELDB_ONCE_INIT (0) + +extern void InitOnce(OnceType* once, void (*initializer)()); + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} +} + +#endif // STORAGE_LEVELDB_PORT_PORT_WIN_H_ diff --git a/src/leveldb/port/win/stdint.h b/src/leveldb/port/win/stdint.h new file mode 100644 index 0000000000..39edd0db13 --- /dev/null +++ b/src/leveldb/port/win/stdint.h @@ -0,0 +1,24 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// MSVC didn't ship with this file until the 2010 version. + +#ifndef STORAGE_LEVELDB_PORT_WIN_STDINT_H_ +#define STORAGE_LEVELDB_PORT_WIN_STDINT_H_ + +#if !defined(_MSC_VER) +#error This file should only be included when compiling with MSVC. +#endif + +// Define C99 equivalent types. +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif // STORAGE_LEVELDB_PORT_WIN_STDINT_H_ diff --git a/src/leveldb/table/block.cc b/src/leveldb/table/block.cc new file mode 100644 index 0000000000..199d453773 --- /dev/null +++ b/src/leveldb/table/block.cc @@ -0,0 +1,267 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Decodes the blocks generated by block_builder.cc. + +#include "table/block.h" + +#include <vector> +#include <algorithm> +#include "leveldb/comparator.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +inline uint32_t Block::NumRestarts() const { + assert(size_ >= 2*sizeof(uint32_t)); + return DecodeFixed32(data_ + size_ - sizeof(uint32_t)); +} + +Block::Block(const BlockContents& contents) + : data_(contents.data.data()), + size_(contents.data.size()), + owned_(contents.heap_allocated) { + if (size_ < sizeof(uint32_t)) { + size_ = 0; // Error marker + } else { + restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t); + if (restart_offset_ > size_ - sizeof(uint32_t)) { + // The size is too small for NumRestarts() and therefore + // restart_offset_ wrapped around. + size_ = 0; + } + } +} + +Block::~Block() { + if (owned_) { + delete[] data_; + } +} + +// Helper routine: decode the next block entry starting at "p", +// storing the number of shared key bytes, non_shared key bytes, +// and the length of the value in "*shared", "*non_shared", and +// "*value_length", respectively. Will not derefence past "limit". +// +// If any errors are detected, returns NULL. Otherwise, returns a +// pointer to the key delta (just past the three decoded values). +static inline const char* DecodeEntry(const char* p, const char* limit, + uint32_t* shared, + uint32_t* non_shared, + uint32_t* value_length) { + if (limit - p < 3) return NULL; + *shared = reinterpret_cast<const unsigned char*>(p)[0]; + *non_shared = reinterpret_cast<const unsigned char*>(p)[1]; + *value_length = reinterpret_cast<const unsigned char*>(p)[2]; + if ((*shared | *non_shared | *value_length) < 128) { + // Fast path: all three values are encoded in one byte each + p += 3; + } else { + if ((p = GetVarint32Ptr(p, limit, shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, non_shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, value_length)) == NULL) return NULL; + } + + if (static_cast<uint32_t>(limit - p) < (*non_shared + *value_length)) { + return NULL; + } + return p; +} + +class Block::Iter : public Iterator { + private: + const Comparator* const comparator_; + const char* const data_; // underlying block contents + uint32_t const restarts_; // Offset of restart array (list of fixed32) + uint32_t const num_restarts_; // Number of uint32_t entries in restart array + + // current_ is offset in data_ of current entry. >= restarts_ if !Valid + uint32_t current_; + uint32_t restart_index_; // Index of restart block in which current_ falls + std::string key_; + Slice value_; + Status status_; + + inline int Compare(const Slice& a, const Slice& b) const { + return comparator_->Compare(a, b); + } + + // Return the offset in data_ just past the end of the current entry. + inline uint32_t NextEntryOffset() const { + return (value_.data() + value_.size()) - data_; + } + + uint32_t GetRestartPoint(uint32_t index) { + assert(index < num_restarts_); + return DecodeFixed32(data_ + restarts_ + index * sizeof(uint32_t)); + } + + void SeekToRestartPoint(uint32_t index) { + key_.clear(); + restart_index_ = index; + // current_ will be fixed by ParseNextKey(); + + // ParseNextKey() starts at the end of value_, so set value_ accordingly + uint32_t offset = GetRestartPoint(index); + value_ = Slice(data_ + offset, 0); + } + + public: + Iter(const Comparator* comparator, + const char* data, + uint32_t restarts, + uint32_t num_restarts) + : comparator_(comparator), + data_(data), + restarts_(restarts), + num_restarts_(num_restarts), + current_(restarts_), + restart_index_(num_restarts_) { + assert(num_restarts_ > 0); + } + + virtual bool Valid() const { return current_ < restarts_; } + virtual Status status() const { return status_; } + virtual Slice key() const { + assert(Valid()); + return key_; + } + virtual Slice value() const { + assert(Valid()); + return value_; + } + + virtual void Next() { + assert(Valid()); + ParseNextKey(); + } + + virtual void Prev() { + assert(Valid()); + + // Scan backwards to a restart point before current_ + const uint32_t original = current_; + while (GetRestartPoint(restart_index_) >= original) { + if (restart_index_ == 0) { + // No more entries + current_ = restarts_; + restart_index_ = num_restarts_; + return; + } + restart_index_--; + } + + SeekToRestartPoint(restart_index_); + do { + // Loop until end of current entry hits the start of original entry + } while (ParseNextKey() && NextEntryOffset() < original); + } + + virtual void Seek(const Slice& target) { + // Binary search in restart array to find the first restart point + // with a key >= target + uint32_t left = 0; + uint32_t right = num_restarts_ - 1; + while (left < right) { + uint32_t mid = (left + right + 1) / 2; + uint32_t region_offset = GetRestartPoint(mid); + uint32_t shared, non_shared, value_length; + const char* key_ptr = DecodeEntry(data_ + region_offset, + data_ + restarts_, + &shared, &non_shared, &value_length); + if (key_ptr == NULL || (shared != 0)) { + CorruptionError(); + return; + } + Slice mid_key(key_ptr, non_shared); + if (Compare(mid_key, target) < 0) { + // Key at "mid" is smaller than "target". Therefore all + // blocks before "mid" are uninteresting. + left = mid; + } else { + // Key at "mid" is >= "target". Therefore all blocks at or + // after "mid" are uninteresting. + right = mid - 1; + } + } + + // Linear search (within restart block) for first key >= target + SeekToRestartPoint(left); + while (true) { + if (!ParseNextKey()) { + return; + } + if (Compare(key_, target) >= 0) { + return; + } + } + } + + virtual void SeekToFirst() { + SeekToRestartPoint(0); + ParseNextKey(); + } + + virtual void SeekToLast() { + SeekToRestartPoint(num_restarts_ - 1); + while (ParseNextKey() && NextEntryOffset() < restarts_) { + // Keep skipping + } + } + + private: + void CorruptionError() { + current_ = restarts_; + restart_index_ = num_restarts_; + status_ = Status::Corruption("bad entry in block"); + key_.clear(); + value_.clear(); + } + + bool ParseNextKey() { + current_ = NextEntryOffset(); + const char* p = data_ + current_; + const char* limit = data_ + restarts_; // Restarts come right after data + if (p >= limit) { + // No more entries to return. Mark as invalid. + current_ = restarts_; + restart_index_ = num_restarts_; + return false; + } + + // Decode next entry + uint32_t shared, non_shared, value_length; + p = DecodeEntry(p, limit, &shared, &non_shared, &value_length); + if (p == NULL || key_.size() < shared) { + CorruptionError(); + return false; + } else { + key_.resize(shared); + key_.append(p, non_shared); + value_ = Slice(p + non_shared, value_length); + while (restart_index_ + 1 < num_restarts_ && + GetRestartPoint(restart_index_ + 1) < current_) { + ++restart_index_; + } + return true; + } + } +}; + +Iterator* Block::NewIterator(const Comparator* cmp) { + if (size_ < 2*sizeof(uint32_t)) { + return NewErrorIterator(Status::Corruption("bad block contents")); + } + const uint32_t num_restarts = NumRestarts(); + if (num_restarts == 0) { + return NewEmptyIterator(); + } else { + return new Iter(cmp, data_, restart_offset_, num_restarts); + } +} + +} // namespace leveldb diff --git a/src/leveldb/table/block.h b/src/leveldb/table/block.h new file mode 100644 index 0000000000..2493eb9f9f --- /dev/null +++ b/src/leveldb/table/block.h @@ -0,0 +1,44 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_H_ + +#include <stddef.h> +#include <stdint.h> +#include "leveldb/iterator.h" + +namespace leveldb { + +struct BlockContents; +class Comparator; + +class Block { + public: + // Initialize the block with the specified contents. + explicit Block(const BlockContents& contents); + + ~Block(); + + size_t size() const { return size_; } + Iterator* NewIterator(const Comparator* comparator); + + private: + uint32_t NumRestarts() const; + + const char* data_; + size_t size_; + uint32_t restart_offset_; // Offset in data_ of restart array + bool owned_; // Block owns data_[] + + // No copying allowed + Block(const Block&); + void operator=(const Block&); + + class Iter; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_H_ diff --git a/src/leveldb/table/block_builder.cc b/src/leveldb/table/block_builder.cc new file mode 100644 index 0000000000..db660cd07c --- /dev/null +++ b/src/leveldb/table/block_builder.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// BlockBuilder generates blocks where keys are prefix-compressed: +// +// When we store a key, we drop the prefix shared with the previous +// string. This helps reduce the space requirement significantly. +// Furthermore, once every K keys, we do not apply the prefix +// compression and store the entire key. We call this a "restart +// point". The tail end of the block stores the offsets of all of the +// restart points, and can be used to do a binary search when looking +// for a particular key. Values are stored as-is (without compression) +// immediately following the corresponding key. +// +// An entry for a particular key-value pair has the form: +// shared_bytes: varint32 +// unshared_bytes: varint32 +// value_length: varint32 +// key_delta: char[unshared_bytes] +// value: char[value_length] +// shared_bytes == 0 for restart points. +// +// The trailer of the block has the form: +// restarts: uint32[num_restarts] +// num_restarts: uint32 +// restarts[i] contains the offset within the block of the ith restart point. + +#include "table/block_builder.h" + +#include <algorithm> +#include <assert.h> +#include "leveldb/comparator.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" + +namespace leveldb { + +BlockBuilder::BlockBuilder(const Options* options) + : options_(options), + restarts_(), + counter_(0), + finished_(false) { + assert(options->block_restart_interval >= 1); + restarts_.push_back(0); // First restart point is at offset 0 +} + +void BlockBuilder::Reset() { + buffer_.clear(); + restarts_.clear(); + restarts_.push_back(0); // First restart point is at offset 0 + counter_ = 0; + finished_ = false; + last_key_.clear(); +} + +size_t BlockBuilder::CurrentSizeEstimate() const { + return (buffer_.size() + // Raw data buffer + restarts_.size() * sizeof(uint32_t) + // Restart array + sizeof(uint32_t)); // Restart array length +} + +Slice BlockBuilder::Finish() { + // Append restart array + for (size_t i = 0; i < restarts_.size(); i++) { + PutFixed32(&buffer_, restarts_[i]); + } + PutFixed32(&buffer_, restarts_.size()); + finished_ = true; + return Slice(buffer_); +} + +void BlockBuilder::Add(const Slice& key, const Slice& value) { + Slice last_key_piece(last_key_); + assert(!finished_); + assert(counter_ <= options_->block_restart_interval); + assert(buffer_.empty() // No values yet? + || options_->comparator->Compare(key, last_key_piece) > 0); + size_t shared = 0; + if (counter_ < options_->block_restart_interval) { + // See how much sharing to do with previous string + const size_t min_length = std::min(last_key_piece.size(), key.size()); + while ((shared < min_length) && (last_key_piece[shared] == key[shared])) { + shared++; + } + } else { + // Restart compression + restarts_.push_back(buffer_.size()); + counter_ = 0; + } + const size_t non_shared = key.size() - shared; + + // Add "<shared><non_shared><value_size>" to buffer_ + PutVarint32(&buffer_, shared); + PutVarint32(&buffer_, non_shared); + PutVarint32(&buffer_, value.size()); + + // Add string delta to buffer_ followed by value + buffer_.append(key.data() + shared, non_shared); + buffer_.append(value.data(), value.size()); + + // Update state + last_key_.resize(shared); + last_key_.append(key.data() + shared, non_shared); + assert(Slice(last_key_) == key); + counter_++; +} + +} // namespace leveldb diff --git a/src/leveldb/table/block_builder.h b/src/leveldb/table/block_builder.h new file mode 100644 index 0000000000..5b545bd1af --- /dev/null +++ b/src/leveldb/table/block_builder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ + +#include <vector> + +#include <stdint.h> +#include "leveldb/slice.h" + +namespace leveldb { + +struct Options; + +class BlockBuilder { + public: + explicit BlockBuilder(const Options* options); + + // Reset the contents as if the BlockBuilder was just constructed. + void Reset(); + + // REQUIRES: Finish() has not been callled since the last call to Reset(). + // REQUIRES: key is larger than any previously added key + void Add(const Slice& key, const Slice& value); + + // Finish building the block and return a slice that refers to the + // block contents. The returned slice will remain valid for the + // lifetime of this builder or until Reset() is called. + Slice Finish(); + + // Returns an estimate of the current (uncompressed) size of the block + // we are building. + size_t CurrentSizeEstimate() const; + + // Return true iff no entries have been added since the last Reset() + bool empty() const { + return buffer_.empty(); + } + + private: + const Options* options_; + std::string buffer_; // Destination buffer + std::vector<uint32_t> restarts_; // Restart points + int counter_; // Number of entries emitted since restart + bool finished_; // Has Finish() been called? + std::string last_key_; + + // No copying allowed + BlockBuilder(const BlockBuilder&); + void operator=(const BlockBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ diff --git a/src/leveldb/table/filter_block.cc b/src/leveldb/table/filter_block.cc new file mode 100644 index 0000000000..203e15c8bc --- /dev/null +++ b/src/leveldb/table/filter_block.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" + +namespace leveldb { + +// See doc/table_format.txt for an explanation of the filter block format. + +// Generate new filter every 2KB of data +static const size_t kFilterBaseLg = 11; +static const size_t kFilterBase = 1 << kFilterBaseLg; + +FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy) + : policy_(policy) { +} + +void FilterBlockBuilder::StartBlock(uint64_t block_offset) { + uint64_t filter_index = (block_offset / kFilterBase); + assert(filter_index >= filter_offsets_.size()); + while (filter_index > filter_offsets_.size()) { + GenerateFilter(); + } +} + +void FilterBlockBuilder::AddKey(const Slice& key) { + Slice k = key; + start_.push_back(keys_.size()); + keys_.append(k.data(), k.size()); +} + +Slice FilterBlockBuilder::Finish() { + if (!start_.empty()) { + GenerateFilter(); + } + + // Append array of per-filter offsets + const uint32_t array_offset = result_.size(); + for (size_t i = 0; i < filter_offsets_.size(); i++) { + PutFixed32(&result_, filter_offsets_[i]); + } + + PutFixed32(&result_, array_offset); + result_.push_back(kFilterBaseLg); // Save encoding parameter in result + return Slice(result_); +} + +void FilterBlockBuilder::GenerateFilter() { + const size_t num_keys = start_.size(); + if (num_keys == 0) { + // Fast path if there are no keys for this filter + filter_offsets_.push_back(result_.size()); + return; + } + + // Make list of keys from flattened key structure + start_.push_back(keys_.size()); // Simplify length computation + tmp_keys_.resize(num_keys); + for (size_t i = 0; i < num_keys; i++) { + const char* base = keys_.data() + start_[i]; + size_t length = start_[i+1] - start_[i]; + tmp_keys_[i] = Slice(base, length); + } + + // Generate filter for current set of keys and append to result_. + filter_offsets_.push_back(result_.size()); + policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_); + + tmp_keys_.clear(); + keys_.clear(); + start_.clear(); +} + +FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, + const Slice& contents) + : policy_(policy), + data_(NULL), + offset_(NULL), + num_(0), + base_lg_(0) { + size_t n = contents.size(); + if (n < 5) return; // 1 byte for base_lg_ and 4 for start of offset array + base_lg_ = contents[n-1]; + uint32_t last_word = DecodeFixed32(contents.data() + n - 5); + if (last_word > n - 5) return; + data_ = contents.data(); + offset_ = data_ + last_word; + num_ = (n - 5 - last_word) / 4; +} + +bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { + uint64_t index = block_offset >> base_lg_; + if (index < num_) { + uint32_t start = DecodeFixed32(offset_ + index*4); + uint32_t limit = DecodeFixed32(offset_ + index*4 + 4); + if (start <= limit && limit <= (offset_ - data_)) { + Slice filter = Slice(data_ + start, limit - start); + return policy_->KeyMayMatch(key, filter); + } else if (start == limit) { + // Empty filters do not match any keys + return false; + } + } + return true; // Errors are treated as potential matches +} + +} diff --git a/src/leveldb/table/filter_block.h b/src/leveldb/table/filter_block.h new file mode 100644 index 0000000000..c67d010bd1 --- /dev/null +++ b/src/leveldb/table/filter_block.h @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A filter block is stored near the end of a Table file. It contains +// filters (e.g., bloom filters) for all data blocks in the table combined +// into a single filter block. + +#ifndef STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ + +#include <stddef.h> +#include <stdint.h> +#include <string> +#include <vector> +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +class FilterPolicy; + +// A FilterBlockBuilder is used to construct all of the filters for a +// particular Table. It generates a single string which is stored as +// a special block in the Table. +// +// The sequence of calls to FilterBlockBuilder must match the regexp: +// (StartBlock AddKey*)* Finish +class FilterBlockBuilder { + public: + explicit FilterBlockBuilder(const FilterPolicy*); + + void StartBlock(uint64_t block_offset); + void AddKey(const Slice& key); + Slice Finish(); + + private: + void GenerateFilter(); + + const FilterPolicy* policy_; + std::string keys_; // Flattened key contents + std::vector<size_t> start_; // Starting index in keys_ of each key + std::string result_; // Filter data computed so far + std::vector<Slice> tmp_keys_; // policy_->CreateFilter() argument + std::vector<uint32_t> filter_offsets_; + + // No copying allowed + FilterBlockBuilder(const FilterBlockBuilder&); + void operator=(const FilterBlockBuilder&); +}; + +class FilterBlockReader { + public: + // REQUIRES: "contents" and *policy must stay live while *this is live. + FilterBlockReader(const FilterPolicy* policy, const Slice& contents); + bool KeyMayMatch(uint64_t block_offset, const Slice& key); + + private: + const FilterPolicy* policy_; + const char* data_; // Pointer to filter data (at block-start) + const char* offset_; // Pointer to beginning of offset array (at block-end) + size_t num_; // Number of entries in offset array + size_t base_lg_; // Encoding parameter (see kFilterBaseLg in .cc file) +}; + +} + +#endif // STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ diff --git a/src/leveldb/table/filter_block_test.cc b/src/leveldb/table/filter_block_test.cc new file mode 100644 index 0000000000..3a2a07cf53 --- /dev/null +++ b/src/leveldb/table/filter_block_test.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// For testing: emit an array with one hash value per key +class TestHashFilter : public FilterPolicy { + public: + virtual const char* Name() const { + return "TestHashFilter"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + for (int i = 0; i < n; i++) { + uint32_t h = Hash(keys[i].data(), keys[i].size(), 1); + PutFixed32(dst, h); + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + uint32_t h = Hash(key.data(), key.size(), 1); + for (int i = 0; i + 4 <= filter.size(); i += 4) { + if (h == DecodeFixed32(filter.data() + i)) { + return true; + } + } + return false; + } +}; + +class FilterBlockTest { + public: + TestHashFilter policy_; +}; + +TEST(FilterBlockTest, EmptyBuilder) { + FilterBlockBuilder builder(&policy_); + Slice block = builder.Finish(); + ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block)); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100000, "foo")); +} + +TEST(FilterBlockTest, SingleChunk) { + FilterBlockBuilder builder(&policy_); + builder.StartBlock(100); + builder.AddKey("foo"); + builder.AddKey("bar"); + builder.AddKey("box"); + builder.StartBlock(200); + builder.AddKey("box"); + builder.StartBlock(300); + builder.AddKey("hello"); + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100, "bar")); + ASSERT_TRUE(reader.KeyMayMatch(100, "box")); + ASSERT_TRUE(reader.KeyMayMatch(100, "hello")); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "missing")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "other")); +} + +TEST(FilterBlockTest, MultiChunk) { + FilterBlockBuilder builder(&policy_); + + // First filter + builder.StartBlock(0); + builder.AddKey("foo"); + builder.StartBlock(2000); + builder.AddKey("bar"); + + // Second filter + builder.StartBlock(3100); + builder.AddKey("box"); + + // Third filter is empty + + // Last filter + builder.StartBlock(9000); + builder.AddKey("box"); + builder.AddKey("hello"); + + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + + // Check first filter + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(2000, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "hello")); + + // Check second filter + ASSERT_TRUE(reader.KeyMayMatch(3100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "hello")); + + // Check third filter (empty) + ASSERT_TRUE(! reader.KeyMayMatch(4100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "hello")); + + // Check last filter + ASSERT_TRUE(reader.KeyMayMatch(9000, "box")); + ASSERT_TRUE(reader.KeyMayMatch(9000, "hello")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "bar")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/table/format.cc b/src/leveldb/table/format.cc new file mode 100644 index 0000000000..cda1decdf3 --- /dev/null +++ b/src/leveldb/table/format.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/format.h" + +#include "leveldb/env.h" +#include "port/port.h" +#include "table/block.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +void BlockHandle::EncodeTo(std::string* dst) const { + // Sanity check that all fields have been set + assert(offset_ != ~static_cast<uint64_t>(0)); + assert(size_ != ~static_cast<uint64_t>(0)); + PutVarint64(dst, offset_); + PutVarint64(dst, size_); +} + +Status BlockHandle::DecodeFrom(Slice* input) { + if (GetVarint64(input, &offset_) && + GetVarint64(input, &size_)) { + return Status::OK(); + } else { + return Status::Corruption("bad block handle"); + } +} + +void Footer::EncodeTo(std::string* dst) const { +#ifndef NDEBUG + const size_t original_size = dst->size(); +#endif + metaindex_handle_.EncodeTo(dst); + index_handle_.EncodeTo(dst); + dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding + PutFixed32(dst, static_cast<uint32_t>(kTableMagicNumber & 0xffffffffu)); + PutFixed32(dst, static_cast<uint32_t>(kTableMagicNumber >> 32)); + assert(dst->size() == original_size + kEncodedLength); +} + +Status Footer::DecodeFrom(Slice* input) { + const char* magic_ptr = input->data() + kEncodedLength - 8; + const uint32_t magic_lo = DecodeFixed32(magic_ptr); + const uint32_t magic_hi = DecodeFixed32(magic_ptr + 4); + const uint64_t magic = ((static_cast<uint64_t>(magic_hi) << 32) | + (static_cast<uint64_t>(magic_lo))); + if (magic != kTableMagicNumber) { + return Status::InvalidArgument("not an sstable (bad magic number)"); + } + + Status result = metaindex_handle_.DecodeFrom(input); + if (result.ok()) { + result = index_handle_.DecodeFrom(input); + } + if (result.ok()) { + // We skip over any leftover data (just padding for now) in "input" + const char* end = magic_ptr + 8; + *input = Slice(end, input->data() + input->size() - end); + } + return result; +} + +Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result) { + result->data = Slice(); + result->cachable = false; + result->heap_allocated = false; + + // Read the block contents as well as the type/crc footer. + // See table_builder.cc for the code that built this structure. + size_t n = static_cast<size_t>(handle.size()); + char* buf = new char[n + kBlockTrailerSize]; + Slice contents; + Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf); + if (!s.ok()) { + delete[] buf; + return s; + } + if (contents.size() != n + kBlockTrailerSize) { + delete[] buf; + return Status::Corruption("truncated block read"); + } + + // Check the crc of the type and the block contents + const char* data = contents.data(); // Pointer to where Read put the data + if (options.verify_checksums) { + const uint32_t crc = crc32c::Unmask(DecodeFixed32(data + n + 1)); + const uint32_t actual = crc32c::Value(data, n + 1); + if (actual != crc) { + delete[] buf; + s = Status::Corruption("block checksum mismatch"); + return s; + } + } + + switch (data[n]) { + case kNoCompression: + if (data != buf) { + // File implementation gave us pointer to some other data. + // Use it directly under the assumption that it will be live + // while the file is open. + delete[] buf; + result->data = Slice(data, n); + result->heap_allocated = false; + result->cachable = false; // Do not double-cache + } else { + result->data = Slice(buf, n); + result->heap_allocated = true; + result->cachable = true; + } + + // Ok + break; + case kSnappyCompression: { + size_t ulength = 0; + if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { + delete[] buf; + return Status::Corruption("corrupted compressed block contents"); + } + char* ubuf = new char[ulength]; + if (!port::Snappy_Uncompress(data, n, ubuf)) { + delete[] buf; + delete[] ubuf; + return Status::Corruption("corrupted compressed block contents"); + } + delete[] buf; + result->data = Slice(ubuf, ulength); + result->heap_allocated = true; + result->cachable = true; + break; + } + default: + delete[] buf; + return Status::Corruption("bad block type"); + } + + return Status::OK(); +} + +} // namespace leveldb diff --git a/src/leveldb/table/format.h b/src/leveldb/table/format.h new file mode 100644 index 0000000000..6c0b80c017 --- /dev/null +++ b/src/leveldb/table/format.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_FORMAT_H_ +#define STORAGE_LEVELDB_TABLE_FORMAT_H_ + +#include <string> +#include <stdint.h> +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "leveldb/table_builder.h" + +namespace leveldb { + +class Block; +class RandomAccessFile; +struct ReadOptions; + +// BlockHandle is a pointer to the extent of a file that stores a data +// block or a meta block. +class BlockHandle { + public: + BlockHandle(); + + // The offset of the block in the file. + uint64_t offset() const { return offset_; } + void set_offset(uint64_t offset) { offset_ = offset; } + + // The size of the stored block + uint64_t size() const { return size_; } + void set_size(uint64_t size) { size_ = size; } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Maximum encoding length of a BlockHandle + enum { kMaxEncodedLength = 10 + 10 }; + + private: + uint64_t offset_; + uint64_t size_; +}; + +// Footer encapsulates the fixed information stored at the tail +// end of every table file. +class Footer { + public: + Footer() { } + + // The block handle for the metaindex block of the table + const BlockHandle& metaindex_handle() const { return metaindex_handle_; } + void set_metaindex_handle(const BlockHandle& h) { metaindex_handle_ = h; } + + // The block handle for the index block of the table + const BlockHandle& index_handle() const { + return index_handle_; + } + void set_index_handle(const BlockHandle& h) { + index_handle_ = h; + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Encoded length of a Footer. Note that the serialization of a + // Footer will always occupy exactly this many bytes. It consists + // of two block handles and a magic number. + enum { + kEncodedLength = 2*BlockHandle::kMaxEncodedLength + 8 + }; + + private: + BlockHandle metaindex_handle_; + BlockHandle index_handle_; +}; + +// kTableMagicNumber was picked by running +// echo http://code.google.com/p/leveldb/ | sha1sum +// and taking the leading 64 bits. +static const uint64_t kTableMagicNumber = 0xdb4775248b80fb57ull; + +// 1-byte type + 32-bit crc +static const size_t kBlockTrailerSize = 5; + +struct BlockContents { + Slice data; // Actual contents of data + bool cachable; // True iff data can be cached + bool heap_allocated; // True iff caller should delete[] data.data() +}; + +// Read the block identified by "handle" from "file". On failure +// return non-OK. On success fill *result and return OK. +extern Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result); + +// Implementation details follow. Clients should ignore, + +inline BlockHandle::BlockHandle() + : offset_(~static_cast<uint64_t>(0)), + size_(~static_cast<uint64_t>(0)) { +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_FORMAT_H_ diff --git a/src/leveldb/table/iterator.cc b/src/leveldb/table/iterator.cc new file mode 100644 index 0000000000..3d1c87fdec --- /dev/null +++ b/src/leveldb/table/iterator.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/iterator.h" + +namespace leveldb { + +Iterator::Iterator() { + cleanup_.function = NULL; + cleanup_.next = NULL; +} + +Iterator::~Iterator() { + if (cleanup_.function != NULL) { + (*cleanup_.function)(cleanup_.arg1, cleanup_.arg2); + for (Cleanup* c = cleanup_.next; c != NULL; ) { + (*c->function)(c->arg1, c->arg2); + Cleanup* next = c->next; + delete c; + c = next; + } + } +} + +void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) { + assert(func != NULL); + Cleanup* c; + if (cleanup_.function == NULL) { + c = &cleanup_; + } else { + c = new Cleanup; + c->next = cleanup_.next; + cleanup_.next = c; + } + c->function = func; + c->arg1 = arg1; + c->arg2 = arg2; +} + +namespace { +class EmptyIterator : public Iterator { + public: + EmptyIterator(const Status& s) : status_(s) { } + virtual bool Valid() const { return false; } + virtual void Seek(const Slice& target) { } + virtual void SeekToFirst() { } + virtual void SeekToLast() { } + virtual void Next() { assert(false); } + virtual void Prev() { assert(false); } + Slice key() const { assert(false); return Slice(); } + Slice value() const { assert(false); return Slice(); } + virtual Status status() const { return status_; } + private: + Status status_; +}; +} // namespace + +Iterator* NewEmptyIterator() { + return new EmptyIterator(Status::OK()); +} + +Iterator* NewErrorIterator(const Status& status) { + return new EmptyIterator(status); +} + +} // namespace leveldb diff --git a/src/leveldb/table/iterator_wrapper.h b/src/leveldb/table/iterator_wrapper.h new file mode 100644 index 0000000000..9e16b3dbed --- /dev/null +++ b/src/leveldb/table/iterator_wrapper.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ +#define STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ + +namespace leveldb { + +// A internal wrapper class with an interface similar to Iterator that +// caches the valid() and key() results for an underlying iterator. +// This can help avoid virtual function calls and also gives better +// cache locality. +class IteratorWrapper { + public: + IteratorWrapper(): iter_(NULL), valid_(false) { } + explicit IteratorWrapper(Iterator* iter): iter_(NULL) { + Set(iter); + } + ~IteratorWrapper() { delete iter_; } + Iterator* iter() const { return iter_; } + + // Takes ownership of "iter" and will delete it when destroyed, or + // when Set() is invoked again. + void Set(Iterator* iter) { + delete iter_; + iter_ = iter; + if (iter_ == NULL) { + valid_ = false; + } else { + Update(); + } + } + + + // Iterator interface methods + bool Valid() const { return valid_; } + Slice key() const { assert(Valid()); return key_; } + Slice value() const { assert(Valid()); return iter_->value(); } + // Methods below require iter() != NULL + Status status() const { assert(iter_); return iter_->status(); } + void Next() { assert(iter_); iter_->Next(); Update(); } + void Prev() { assert(iter_); iter_->Prev(); Update(); } + void Seek(const Slice& k) { assert(iter_); iter_->Seek(k); Update(); } + void SeekToFirst() { assert(iter_); iter_->SeekToFirst(); Update(); } + void SeekToLast() { assert(iter_); iter_->SeekToLast(); Update(); } + + private: + void Update() { + valid_ = iter_->Valid(); + if (valid_) { + key_ = iter_->key(); + } + } + + Iterator* iter_; + bool valid_; + Slice key_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ diff --git a/src/leveldb/table/merger.cc b/src/leveldb/table/merger.cc new file mode 100644 index 0000000000..2dde4dc21f --- /dev/null +++ b/src/leveldb/table/merger.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/merger.h" + +#include "leveldb/comparator.h" +#include "leveldb/iterator.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { +class MergingIterator : public Iterator { + public: + MergingIterator(const Comparator* comparator, Iterator** children, int n) + : comparator_(comparator), + children_(new IteratorWrapper[n]), + n_(n), + current_(NULL), + direction_(kForward) { + for (int i = 0; i < n; i++) { + children_[i].Set(children[i]); + } + } + + virtual ~MergingIterator() { + delete[] children_; + } + + virtual bool Valid() const { + return (current_ != NULL); + } + + virtual void SeekToFirst() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToFirst(); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void SeekToLast() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToLast(); + } + FindLargest(); + direction_ = kReverse; + } + + virtual void Seek(const Slice& target) { + for (int i = 0; i < n_; i++) { + children_[i].Seek(target); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void Next() { + assert(Valid()); + + // Ensure that all children are positioned after key(). + // If we are moving in the forward direction, it is already + // true for all of the non-current_ children since current_ is + // the smallest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kForward) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid() && + comparator_->Compare(key(), child->key()) == 0) { + child->Next(); + } + } + } + direction_ = kForward; + } + + current_->Next(); + FindSmallest(); + } + + virtual void Prev() { + assert(Valid()); + + // Ensure that all children are positioned before key(). + // If we are moving in the reverse direction, it is already + // true for all of the non-current_ children since current_ is + // the largest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kReverse) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid()) { + // Child is at first entry >= key(). Step back one to be < key() + child->Prev(); + } else { + // Child has no entries >= key(). Position at last entry. + child->SeekToLast(); + } + } + } + direction_ = kReverse; + } + + current_->Prev(); + FindLargest(); + } + + virtual Slice key() const { + assert(Valid()); + return current_->key(); + } + + virtual Slice value() const { + assert(Valid()); + return current_->value(); + } + + virtual Status status() const { + Status status; + for (int i = 0; i < n_; i++) { + status = children_[i].status(); + if (!status.ok()) { + break; + } + } + return status; + } + + private: + void FindSmallest(); + void FindLargest(); + + // We might want to use a heap in case there are lots of children. + // For now we use a simple array since we expect a very small number + // of children in leveldb. + const Comparator* comparator_; + IteratorWrapper* children_; + int n_; + IteratorWrapper* current_; + + // Which direction is the iterator moving? + enum Direction { + kForward, + kReverse + }; + Direction direction_; +}; + +void MergingIterator::FindSmallest() { + IteratorWrapper* smallest = NULL; + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (smallest == NULL) { + smallest = child; + } else if (comparator_->Compare(child->key(), smallest->key()) < 0) { + smallest = child; + } + } + } + current_ = smallest; +} + +void MergingIterator::FindLargest() { + IteratorWrapper* largest = NULL; + for (int i = n_-1; i >= 0; i--) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (largest == NULL) { + largest = child; + } else if (comparator_->Compare(child->key(), largest->key()) > 0) { + largest = child; + } + } + } + current_ = largest; +} +} // namespace + +Iterator* NewMergingIterator(const Comparator* cmp, Iterator** list, int n) { + assert(n >= 0); + if (n == 0) { + return NewEmptyIterator(); + } else if (n == 1) { + return list[0]; + } else { + return new MergingIterator(cmp, list, n); + } +} + +} // namespace leveldb diff --git a/src/leveldb/table/merger.h b/src/leveldb/table/merger.h new file mode 100644 index 0000000000..91ddd80faa --- /dev/null +++ b/src/leveldb/table/merger.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_MERGER_H_ +#define STORAGE_LEVELDB_TABLE_MERGER_H_ + +namespace leveldb { + +class Comparator; +class Iterator; + +// Return an iterator that provided the union of the data in +// children[0,n-1]. Takes ownership of the child iterators and +// will delete them when the result iterator is deleted. +// +// The result does no duplicate suppression. I.e., if a particular +// key is present in K child iterators, it will be yielded K times. +// +// REQUIRES: n >= 0 +extern Iterator* NewMergingIterator( + const Comparator* comparator, Iterator** children, int n); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_MERGER_H_ diff --git a/src/leveldb/table/table.cc b/src/leveldb/table/table.cc new file mode 100644 index 0000000000..dbd6d3a1bf --- /dev/null +++ b/src/leveldb/table/table.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" + +namespace leveldb { + +struct Table::Rep { + ~Rep() { + delete filter; + delete [] filter_data; + delete index_block; + } + + Options options; + Status status; + RandomAccessFile* file; + uint64_t cache_id; + FilterBlockReader* filter; + const char* filter_data; + + BlockHandle metaindex_handle; // Handle to metaindex_block: saved from footer + Block* index_block; +}; + +Status Table::Open(const Options& options, + RandomAccessFile* file, + uint64_t size, + Table** table) { + *table = NULL; + if (size < Footer::kEncodedLength) { + return Status::InvalidArgument("file is too short to be an sstable"); + } + + char footer_space[Footer::kEncodedLength]; + Slice footer_input; + Status s = file->Read(size - Footer::kEncodedLength, Footer::kEncodedLength, + &footer_input, footer_space); + if (!s.ok()) return s; + + Footer footer; + s = footer.DecodeFrom(&footer_input); + if (!s.ok()) return s; + + // Read the index block + BlockContents contents; + Block* index_block = NULL; + if (s.ok()) { + s = ReadBlock(file, ReadOptions(), footer.index_handle(), &contents); + if (s.ok()) { + index_block = new Block(contents); + } + } + + if (s.ok()) { + // We've successfully read the footer and the index block: we're + // ready to serve requests. + Rep* rep = new Table::Rep; + rep->options = options; + rep->file = file; + rep->metaindex_handle = footer.metaindex_handle(); + rep->index_block = index_block; + rep->cache_id = (options.block_cache ? options.block_cache->NewId() : 0); + rep->filter_data = NULL; + rep->filter = NULL; + *table = new Table(rep); + (*table)->ReadMeta(footer); + } else { + if (index_block) delete index_block; + } + + return s; +} + +void Table::ReadMeta(const Footer& footer) { + if (rep_->options.filter_policy == NULL) { + return; // Do not need any metadata + } + + // TODO(sanjay): Skip this if footer.metaindex_handle() size indicates + // it is an empty block. + ReadOptions opt; + BlockContents contents; + if (!ReadBlock(rep_->file, opt, footer.metaindex_handle(), &contents).ok()) { + // Do not propagate errors since meta info is not needed for operation + return; + } + Block* meta = new Block(contents); + + Iterator* iter = meta->NewIterator(BytewiseComparator()); + std::string key = "filter."; + key.append(rep_->options.filter_policy->Name()); + iter->Seek(key); + if (iter->Valid() && iter->key() == Slice(key)) { + ReadFilter(iter->value()); + } + delete iter; + delete meta; +} + +void Table::ReadFilter(const Slice& filter_handle_value) { + Slice v = filter_handle_value; + BlockHandle filter_handle; + if (!filter_handle.DecodeFrom(&v).ok()) { + return; + } + + // We might want to unify with ReadBlock() if we start + // requiring checksum verification in Table::Open. + ReadOptions opt; + BlockContents block; + if (!ReadBlock(rep_->file, opt, filter_handle, &block).ok()) { + return; + } + if (block.heap_allocated) { + rep_->filter_data = block.data.data(); // Will need to delete later + } + rep_->filter = new FilterBlockReader(rep_->options.filter_policy, block.data); +} + +Table::~Table() { + delete rep_; +} + +static void DeleteBlock(void* arg, void* ignored) { + delete reinterpret_cast<Block*>(arg); +} + +static void DeleteCachedBlock(const Slice& key, void* value) { + Block* block = reinterpret_cast<Block*>(value); + delete block; +} + +static void ReleaseBlock(void* arg, void* h) { + Cache* cache = reinterpret_cast<Cache*>(arg); + Cache::Handle* handle = reinterpret_cast<Cache::Handle*>(h); + cache->Release(handle); +} + +// Convert an index iterator value (i.e., an encoded BlockHandle) +// into an iterator over the contents of the corresponding block. +Iterator* Table::BlockReader(void* arg, + const ReadOptions& options, + const Slice& index_value) { + Table* table = reinterpret_cast<Table*>(arg); + Cache* block_cache = table->rep_->options.block_cache; + Block* block = NULL; + Cache::Handle* cache_handle = NULL; + + BlockHandle handle; + Slice input = index_value; + Status s = handle.DecodeFrom(&input); + // We intentionally allow extra stuff in index_value so that we + // can add more features in the future. + + if (s.ok()) { + BlockContents contents; + if (block_cache != NULL) { + char cache_key_buffer[16]; + EncodeFixed64(cache_key_buffer, table->rep_->cache_id); + EncodeFixed64(cache_key_buffer+8, handle.offset()); + Slice key(cache_key_buffer, sizeof(cache_key_buffer)); + cache_handle = block_cache->Lookup(key); + if (cache_handle != NULL) { + block = reinterpret_cast<Block*>(block_cache->Value(cache_handle)); + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + if (contents.cachable && options.fill_cache) { + cache_handle = block_cache->Insert( + key, block, block->size(), &DeleteCachedBlock); + } + } + } + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + } + } + } + + Iterator* iter; + if (block != NULL) { + iter = block->NewIterator(table->rep_->options.comparator); + if (cache_handle == NULL) { + iter->RegisterCleanup(&DeleteBlock, block, NULL); + } else { + iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle); + } + } else { + iter = NewErrorIterator(s); + } + return iter; +} + +Iterator* Table::NewIterator(const ReadOptions& options) const { + return NewTwoLevelIterator( + rep_->index_block->NewIterator(rep_->options.comparator), + &Table::BlockReader, const_cast<Table*>(this), options); +} + +Status Table::InternalGet(const ReadOptions& options, const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Status s; + Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator); + iiter->Seek(k); + if (iiter->Valid()) { + Slice handle_value = iiter->value(); + FilterBlockReader* filter = rep_->filter; + BlockHandle handle; + if (filter != NULL && + handle.DecodeFrom(&handle_value).ok() && + !filter->KeyMayMatch(handle.offset(), k)) { + // Not found + } else { + Slice handle = iiter->value(); + Iterator* block_iter = BlockReader(this, options, iiter->value()); + block_iter->Seek(k); + if (block_iter->Valid()) { + (*saver)(arg, block_iter->key(), block_iter->value()); + } + s = block_iter->status(); + delete block_iter; + } + } + if (s.ok()) { + s = iiter->status(); + } + delete iiter; + return s; +} + + +uint64_t Table::ApproximateOffsetOf(const Slice& key) const { + Iterator* index_iter = + rep_->index_block->NewIterator(rep_->options.comparator); + index_iter->Seek(key); + uint64_t result; + if (index_iter->Valid()) { + BlockHandle handle; + Slice input = index_iter->value(); + Status s = handle.DecodeFrom(&input); + if (s.ok()) { + result = handle.offset(); + } else { + // Strange: we can't decode the block handle in the index block. + // We'll just return the offset of the metaindex block, which is + // close to the whole file size for this case. + result = rep_->metaindex_handle.offset(); + } + } else { + // key is past the last key in the file. Approximate the offset + // by returning the offset of the metaindex block (which is + // right near the end of the file). + result = rep_->metaindex_handle.offset(); + } + delete index_iter; + return result; +} + +} // namespace leveldb diff --git a/src/leveldb/table/table_builder.cc b/src/leveldb/table/table_builder.cc new file mode 100644 index 0000000000..62002c84f2 --- /dev/null +++ b/src/leveldb/table/table_builder.cc @@ -0,0 +1,270 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table_builder.h" + +#include <assert.h> +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block_builder.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +struct TableBuilder::Rep { + Options options; + Options index_block_options; + WritableFile* file; + uint64_t offset; + Status status; + BlockBuilder data_block; + BlockBuilder index_block; + std::string last_key; + int64_t num_entries; + bool closed; // Either Finish() or Abandon() has been called. + FilterBlockBuilder* filter_block; + + // We do not emit the index entry for a block until we have seen the + // first key for the next data block. This allows us to use shorter + // keys in the index block. For example, consider a block boundary + // between the keys "the quick brown fox" and "the who". We can use + // "the r" as the key for the index block entry since it is >= all + // entries in the first block and < all entries in subsequent + // blocks. + // + // Invariant: r->pending_index_entry is true only if data_block is empty. + bool pending_index_entry; + BlockHandle pending_handle; // Handle to add to index block + + std::string compressed_output; + + Rep(const Options& opt, WritableFile* f) + : options(opt), + index_block_options(opt), + file(f), + offset(0), + data_block(&options), + index_block(&index_block_options), + num_entries(0), + closed(false), + filter_block(opt.filter_policy == NULL ? NULL + : new FilterBlockBuilder(opt.filter_policy)), + pending_index_entry(false) { + index_block_options.block_restart_interval = 1; + } +}; + +TableBuilder::TableBuilder(const Options& options, WritableFile* file) + : rep_(new Rep(options, file)) { + if (rep_->filter_block != NULL) { + rep_->filter_block->StartBlock(0); + } +} + +TableBuilder::~TableBuilder() { + assert(rep_->closed); // Catch errors where caller forgot to call Finish() + delete rep_->filter_block; + delete rep_; +} + +Status TableBuilder::ChangeOptions(const Options& options) { + // Note: if more fields are added to Options, update + // this function to catch changes that should not be allowed to + // change in the middle of building a Table. + if (options.comparator != rep_->options.comparator) { + return Status::InvalidArgument("changing comparator while building table"); + } + + // Note that any live BlockBuilders point to rep_->options and therefore + // will automatically pick up the updated options. + rep_->options = options; + rep_->index_block_options = options; + rep_->index_block_options.block_restart_interval = 1; + return Status::OK(); +} + +void TableBuilder::Add(const Slice& key, const Slice& value) { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->num_entries > 0) { + assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0); + } + + if (r->pending_index_entry) { + assert(r->data_block.empty()); + r->options.comparator->FindShortestSeparator(&r->last_key, key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + + if (r->filter_block != NULL) { + r->filter_block->AddKey(key); + } + + r->last_key.assign(key.data(), key.size()); + r->num_entries++; + r->data_block.Add(key, value); + + const size_t estimated_block_size = r->data_block.CurrentSizeEstimate(); + if (estimated_block_size >= r->options.block_size) { + Flush(); + } +} + +void TableBuilder::Flush() { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->data_block.empty()) return; + assert(!r->pending_index_entry); + WriteBlock(&r->data_block, &r->pending_handle); + if (ok()) { + r->pending_index_entry = true; + r->status = r->file->Flush(); + } + if (r->filter_block != NULL) { + r->filter_block->StartBlock(r->offset); + } +} + +void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) { + // File format contains a sequence of blocks where each block has: + // block_data: uint8[n] + // type: uint8 + // crc: uint32 + assert(ok()); + Rep* r = rep_; + Slice raw = block->Finish(); + + Slice block_contents; + CompressionType type = r->options.compression; + // TODO(postrelease): Support more compression options: zlib? + switch (type) { + case kNoCompression: + block_contents = raw; + break; + + case kSnappyCompression: { + std::string* compressed = &r->compressed_output; + if (port::Snappy_Compress(raw.data(), raw.size(), compressed) && + compressed->size() < raw.size() - (raw.size() / 8u)) { + block_contents = *compressed; + } else { + // Snappy not supported, or compressed less than 12.5%, so just + // store uncompressed form + block_contents = raw; + type = kNoCompression; + } + break; + } + } + WriteRawBlock(block_contents, type, handle); + r->compressed_output.clear(); + block->Reset(); +} + +void TableBuilder::WriteRawBlock(const Slice& block_contents, + CompressionType type, + BlockHandle* handle) { + Rep* r = rep_; + handle->set_offset(r->offset); + handle->set_size(block_contents.size()); + r->status = r->file->Append(block_contents); + if (r->status.ok()) { + char trailer[kBlockTrailerSize]; + trailer[0] = type; + uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size()); + crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type + EncodeFixed32(trailer+1, crc32c::Mask(crc)); + r->status = r->file->Append(Slice(trailer, kBlockTrailerSize)); + if (r->status.ok()) { + r->offset += block_contents.size() + kBlockTrailerSize; + } + } +} + +Status TableBuilder::status() const { + return rep_->status; +} + +Status TableBuilder::Finish() { + Rep* r = rep_; + Flush(); + assert(!r->closed); + r->closed = true; + + BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle; + + // Write filter block + if (ok() && r->filter_block != NULL) { + WriteRawBlock(r->filter_block->Finish(), kNoCompression, + &filter_block_handle); + } + + // Write metaindex block + if (ok()) { + BlockBuilder meta_index_block(&r->options); + if (r->filter_block != NULL) { + // Add mapping from "filter.Name" to location of filter data + std::string key = "filter."; + key.append(r->options.filter_policy->Name()); + std::string handle_encoding; + filter_block_handle.EncodeTo(&handle_encoding); + meta_index_block.Add(key, handle_encoding); + } + + // TODO(postrelease): Add stats and other meta blocks + WriteBlock(&meta_index_block, &metaindex_block_handle); + } + + // Write index block + if (ok()) { + if (r->pending_index_entry) { + r->options.comparator->FindShortSuccessor(&r->last_key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + WriteBlock(&r->index_block, &index_block_handle); + } + + // Write footer + if (ok()) { + Footer footer; + footer.set_metaindex_handle(metaindex_block_handle); + footer.set_index_handle(index_block_handle); + std::string footer_encoding; + footer.EncodeTo(&footer_encoding); + r->status = r->file->Append(footer_encoding); + if (r->status.ok()) { + r->offset += footer_encoding.size(); + } + } + return r->status; +} + +void TableBuilder::Abandon() { + Rep* r = rep_; + assert(!r->closed); + r->closed = true; +} + +uint64_t TableBuilder::NumEntries() const { + return rep_->num_entries; +} + +uint64_t TableBuilder::FileSize() const { + return rep_->offset; +} + +} // namespace leveldb diff --git a/src/leveldb/table/table_test.cc b/src/leveldb/table/table_test.cc new file mode 100644 index 0000000000..57cea25334 --- /dev/null +++ b/src/leveldb/table/table_test.cc @@ -0,0 +1,838 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include <map> +#include <string> +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/table_builder.h" +#include "table/block.h" +#include "table/block_builder.h" +#include "table/format.h" +#include "util/random.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// Return reverse of "key". +// Used to test non-lexicographic comparators. +static std::string Reverse(const Slice& key) { + std::string str(key.ToString()); + std::string rev(""); + for (std::string::reverse_iterator rit = str.rbegin(); + rit != str.rend(); ++rit) { + rev.push_back(*rit); + } + return rev; +} + +namespace { +class ReverseKeyComparator : public Comparator { + public: + virtual const char* Name() const { + return "leveldb.ReverseBytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(Reverse(a), Reverse(b)); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + std::string s = Reverse(*start); + std::string l = Reverse(limit); + BytewiseComparator()->FindShortestSeparator(&s, l); + *start = Reverse(s); + } + + virtual void FindShortSuccessor(std::string* key) const { + std::string s = Reverse(*key); + BytewiseComparator()->FindShortSuccessor(&s); + *key = Reverse(s); + } +}; +} // namespace +static ReverseKeyComparator reverse_key_comparator; + +static void Increment(const Comparator* cmp, std::string* key) { + if (cmp == BytewiseComparator()) { + key->push_back('\0'); + } else { + assert(cmp == &reverse_key_comparator); + std::string rev = Reverse(*key); + rev.push_back('\0'); + *key = Reverse(rev); + } +} + +// An STL comparator that uses a Comparator +namespace { +struct STLLessThan { + const Comparator* cmp; + + STLLessThan() : cmp(BytewiseComparator()) { } + STLLessThan(const Comparator* c) : cmp(c) { } + bool operator()(const std::string& a, const std::string& b) const { + return cmp->Compare(Slice(a), Slice(b)) < 0; + } +}; +} // namespace + +class StringSink: public WritableFile { + public: + ~StringSink() { } + + const std::string& contents() const { return contents_; } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + virtual Status Append(const Slice& data) { + contents_.append(data.data(), data.size()); + return Status::OK(); + } + + private: + std::string contents_; +}; + + +class StringSource: public RandomAccessFile { + public: + StringSource(const Slice& contents) + : contents_(contents.data(), contents.size()) { + } + + virtual ~StringSource() { } + + uint64_t Size() const { return contents_.size(); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + if (offset > contents_.size()) { + return Status::InvalidArgument("invalid Read offset"); + } + if (offset + n > contents_.size()) { + n = contents_.size() - offset; + } + memcpy(scratch, &contents_[offset], n); + *result = Slice(scratch, n); + return Status::OK(); + } + + private: + std::string contents_; +}; + +typedef std::map<std::string, std::string, STLLessThan> KVMap; + +// Helper class for tests to unify the interface between +// BlockBuilder/TableBuilder and Block/Table. +class Constructor { + public: + explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) { } + virtual ~Constructor() { } + + void Add(const std::string& key, const Slice& value) { + data_[key] = value.ToString(); + } + + // Finish constructing the data structure with all the keys that have + // been added so far. Returns the keys in sorted order in "*keys" + // and stores the key/value pairs in "*kvmap" + void Finish(const Options& options, + std::vector<std::string>* keys, + KVMap* kvmap) { + *kvmap = data_; + keys->clear(); + for (KVMap::const_iterator it = data_.begin(); + it != data_.end(); + ++it) { + keys->push_back(it->first); + } + data_.clear(); + Status s = FinishImpl(options, *kvmap); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + // Construct the data structure from the data in "data" + virtual Status FinishImpl(const Options& options, const KVMap& data) = 0; + + virtual Iterator* NewIterator() const = 0; + + virtual const KVMap& data() { return data_; } + + virtual DB* db() const { return NULL; } // Overridden in DBConstructor + + private: + KVMap data_; +}; + +class BlockConstructor: public Constructor { + public: + explicit BlockConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp), + block_(NULL) { } + ~BlockConstructor() { + delete block_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete block_; + block_ = NULL; + BlockBuilder builder(&options); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + } + // Open the block + data_ = builder.Finish().ToString(); + BlockContents contents; + contents.data = data_; + contents.cachable = false; + contents.heap_allocated = false; + block_ = new Block(contents); + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return block_->NewIterator(comparator_); + } + + private: + const Comparator* comparator_; + std::string data_; + Block* block_; + + BlockConstructor(); +}; + +class TableConstructor: public Constructor { + public: + TableConstructor(const Comparator* cmp) + : Constructor(cmp), + source_(NULL), table_(NULL) { + } + ~TableConstructor() { + Reset(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + Reset(); + StringSink sink; + TableBuilder builder(options, &sink); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + ASSERT_TRUE(builder.status().ok()); + } + Status s = builder.Finish(); + ASSERT_TRUE(s.ok()) << s.ToString(); + + ASSERT_EQ(sink.contents().size(), builder.FileSize()); + + // Open the table + source_ = new StringSource(sink.contents()); + Options table_options; + table_options.comparator = options.comparator; + return Table::Open(table_options, source_, sink.contents().size(), &table_); + } + + virtual Iterator* NewIterator() const { + return table_->NewIterator(ReadOptions()); + } + + uint64_t ApproximateOffsetOf(const Slice& key) const { + return table_->ApproximateOffsetOf(key); + } + + private: + void Reset() { + delete table_; + delete source_; + table_ = NULL; + source_ = NULL; + } + + StringSource* source_; + Table* table_; + + TableConstructor(); +}; + +// A helper class that converts internal format keys into user keys +class KeyConvertingIterator: public Iterator { + public: + explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) { } + virtual ~KeyConvertingIterator() { delete iter_; } + virtual bool Valid() const { return iter_->Valid(); } + virtual void Seek(const Slice& target) { + ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue); + std::string encoded; + AppendInternalKey(&encoded, ikey); + iter_->Seek(encoded); + } + virtual void SeekToFirst() { iter_->SeekToFirst(); } + virtual void SeekToLast() { iter_->SeekToLast(); } + virtual void Next() { iter_->Next(); } + virtual void Prev() { iter_->Prev(); } + + virtual Slice key() const { + assert(Valid()); + ParsedInternalKey key; + if (!ParseInternalKey(iter_->key(), &key)) { + status_ = Status::Corruption("malformed internal key"); + return Slice("corrupted key"); + } + return key.user_key; + } + + virtual Slice value() const { return iter_->value(); } + virtual Status status() const { + return status_.ok() ? iter_->status() : status_; + } + + private: + mutable Status status_; + Iterator* iter_; + + // No copying allowed + KeyConvertingIterator(const KeyConvertingIterator&); + void operator=(const KeyConvertingIterator&); +}; + +class MemTableConstructor: public Constructor { + public: + explicit MemTableConstructor(const Comparator* cmp) + : Constructor(cmp), + internal_comparator_(cmp) { + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + } + ~MemTableConstructor() { + memtable_->Unref(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + memtable_->Unref(); + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + int seq = 1; + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + memtable_->Add(seq, kTypeValue, it->first, it->second); + seq++; + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return new KeyConvertingIterator(memtable_->NewIterator()); + } + + private: + InternalKeyComparator internal_comparator_; + MemTable* memtable_; +}; + +class DBConstructor: public Constructor { + public: + explicit DBConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp) { + db_ = NULL; + NewDB(); + } + ~DBConstructor() { + delete db_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete db_; + db_ = NULL; + NewDB(); + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + WriteBatch batch; + batch.Put(it->first, it->second); + ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok()); + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return db_->NewIterator(ReadOptions()); + } + + virtual DB* db() const { return db_; } + + private: + void NewDB() { + std::string name = test::TmpDir() + "/table_testdb"; + + Options options; + options.comparator = comparator_; + Status status = DestroyDB(name, options); + ASSERT_TRUE(status.ok()) << status.ToString(); + + options.create_if_missing = true; + options.error_if_exists = true; + options.write_buffer_size = 10000; // Something small to force merging + status = DB::Open(options, name, &db_); + ASSERT_TRUE(status.ok()) << status.ToString(); + } + + const Comparator* comparator_; + DB* db_; +}; + +enum TestType { + TABLE_TEST, + BLOCK_TEST, + MEMTABLE_TEST, + DB_TEST +}; + +struct TestArgs { + TestType type; + bool reverse_compare; + int restart_interval; +}; + +static const TestArgs kTestArgList[] = { + { TABLE_TEST, false, 16 }, + { TABLE_TEST, false, 1 }, + { TABLE_TEST, false, 1024 }, + { TABLE_TEST, true, 16 }, + { TABLE_TEST, true, 1 }, + { TABLE_TEST, true, 1024 }, + + { BLOCK_TEST, false, 16 }, + { BLOCK_TEST, false, 1 }, + { BLOCK_TEST, false, 1024 }, + { BLOCK_TEST, true, 16 }, + { BLOCK_TEST, true, 1 }, + { BLOCK_TEST, true, 1024 }, + + // Restart interval does not matter for memtables + { MEMTABLE_TEST, false, 16 }, + { MEMTABLE_TEST, true, 16 }, + + // Do not bother with restart interval variations for DB + { DB_TEST, false, 16 }, + { DB_TEST, true, 16 }, +}; +static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]); + +class Harness { + public: + Harness() : constructor_(NULL) { } + + void Init(const TestArgs& args) { + delete constructor_; + constructor_ = NULL; + options_ = Options(); + + options_.block_restart_interval = args.restart_interval; + // Use shorter block size for tests to exercise block boundary + // conditions more. + options_.block_size = 256; + if (args.reverse_compare) { + options_.comparator = &reverse_key_comparator; + } + switch (args.type) { + case TABLE_TEST: + constructor_ = new TableConstructor(options_.comparator); + break; + case BLOCK_TEST: + constructor_ = new BlockConstructor(options_.comparator); + break; + case MEMTABLE_TEST: + constructor_ = new MemTableConstructor(options_.comparator); + break; + case DB_TEST: + constructor_ = new DBConstructor(options_.comparator); + break; + } + } + + ~Harness() { + delete constructor_; + } + + void Add(const std::string& key, const std::string& value) { + constructor_->Add(key, value); + } + + void Test(Random* rnd) { + std::vector<std::string> keys; + KVMap data; + constructor_->Finish(options_, &keys, &data); + + TestForwardScan(keys, data); + TestBackwardScan(keys, data); + TestRandomAccess(rnd, keys, data); + } + + void TestForwardScan(const std::vector<std::string>& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToFirst(); + for (KVMap::const_iterator model_iter = data.begin(); + model_iter != data.end(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Next(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestBackwardScan(const std::vector<std::string>& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToLast(); + for (KVMap::const_reverse_iterator model_iter = data.rbegin(); + model_iter != data.rend(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Prev(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestRandomAccess(Random* rnd, + const std::vector<std::string>& keys, + const KVMap& data) { + static const bool kVerbose = false; + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + KVMap::const_iterator model_iter = data.begin(); + if (kVerbose) fprintf(stderr, "---\n"); + for (int i = 0; i < 200; i++) { + const int toss = rnd->Uniform(5); + switch (toss) { + case 0: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Next\n"); + iter->Next(); + ++model_iter; + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 1: { + if (kVerbose) fprintf(stderr, "SeekToFirst\n"); + iter->SeekToFirst(); + model_iter = data.begin(); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 2: { + std::string key = PickRandomKey(rnd, keys); + model_iter = data.lower_bound(key); + if (kVerbose) fprintf(stderr, "Seek '%s'\n", + EscapeString(key).c_str()); + iter->Seek(Slice(key)); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 3: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Prev\n"); + iter->Prev(); + if (model_iter == data.begin()) { + model_iter = data.end(); // Wrap around to invalid value + } else { + --model_iter; + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 4: { + if (kVerbose) fprintf(stderr, "SeekToLast\n"); + iter->SeekToLast(); + if (keys.empty()) { + model_iter = data.end(); + } else { + std::string last = data.rbegin()->first; + model_iter = data.lower_bound(last); + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + } + } + delete iter; + } + + std::string ToString(const KVMap& data, const KVMap::const_iterator& it) { + if (it == data.end()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const KVMap& data, + const KVMap::const_reverse_iterator& it) { + if (it == data.rend()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const Iterator* it) { + if (!it->Valid()) { + return "END"; + } else { + return "'" + it->key().ToString() + "->" + it->value().ToString() + "'"; + } + } + + std::string PickRandomKey(Random* rnd, const std::vector<std::string>& keys) { + if (keys.empty()) { + return "foo"; + } else { + const int index = rnd->Uniform(keys.size()); + std::string result = keys[index]; + switch (rnd->Uniform(3)) { + case 0: + // Return an existing key + break; + case 1: { + // Attempt to return something smaller than an existing key + if (result.size() > 0 && result[result.size()-1] > '\0') { + result[result.size()-1]--; + } + break; + } + case 2: { + // Return something larger than an existing key + Increment(options_.comparator, &result); + break; + } + } + return result; + } + } + + // Returns NULL if not running against a DB + DB* db() const { return constructor_->db(); } + + private: + Options options_; + Constructor* constructor_; +}; + +// Test the empty key +TEST(Harness, SimpleEmptyKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 1); + Add("", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSingle) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 2); + Add("abc", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleMulti) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 3); + Add("abc", "v"); + Add("abcd", "v"); + Add("ac", "v2"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSpecialKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 4); + Add("\xff\xff", "v3"); + Test(&rnd); + } +} + +TEST(Harness, Randomized) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 5); + for (int num_entries = 0; num_entries < 2000; + num_entries += (num_entries < 50 ? 1 : 200)) { + if ((num_entries % 10) == 0) { + fprintf(stderr, "case %d of %d: num_entries = %d\n", + (i + 1), int(kNumTestArgs), num_entries); + } + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + } + } +} + +TEST(Harness, RandomizedLongDB) { + Random rnd(test::RandomSeed()); + TestArgs args = { DB_TEST, false, 16 }; + Init(args); + int num_entries = 100000; + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + + // We must have created enough data to force merging + int files = 0; + for (int level = 0; level < config::kNumLevels; level++) { + std::string value; + char name[100]; + snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level); + ASSERT_TRUE(db()->GetProperty(name, &value)); + files += atoi(value.c_str()); + } + ASSERT_GT(files, 0); +} + +class MemTableTest { }; + +TEST(MemTableTest, Simple) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* memtable = new MemTable(cmp); + memtable->Ref(); + WriteBatch batch; + WriteBatchInternal::SetSequence(&batch, 100); + batch.Put(std::string("k1"), std::string("v1")); + batch.Put(std::string("k2"), std::string("v2")); + batch.Put(std::string("k3"), std::string("v3")); + batch.Put(std::string("largekey"), std::string("vlarge")); + ASSERT_TRUE(WriteBatchInternal::InsertInto(&batch, memtable).ok()); + + Iterator* iter = memtable->NewIterator(); + iter->SeekToFirst(); + while (iter->Valid()) { + fprintf(stderr, "key: '%s' -> '%s'\n", + iter->key().ToString().c_str(), + iter->value().ToString().c_str()); + iter->Next(); + } + + delete iter; + memtable->Unref(); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +class TableTest { }; + +TEST(TableTest, ApproximateOffsetOfPlain) { + TableConstructor c(BytewiseComparator()); + c.Add("k01", "hello"); + c.Add("k02", "hello2"); + c.Add("k03", std::string(10000, 'x')); + c.Add("k04", std::string(200000, 'x')); + c.Add("k05", std::string(300000, 'x')); + c.Add("k06", "hello3"); + c.Add("k07", std::string(100000, 'x')); + std::vector<std::string> keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kNoCompression; + c.Finish(options, &keys, &kvmap); + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); + +} + +static bool SnappyCompressionSupported() { + std::string out; + Slice in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + return port::Snappy_Compress(in.data(), in.size(), &out); +} + +TEST(TableTest, ApproximateOffsetOfCompressed) { + if (!SnappyCompressionSupported()) { + fprintf(stderr, "skipping compression tests\n"); + return; + } + + Random rnd(301); + TableConstructor c(BytewiseComparator()); + std::string tmp; + c.Add("k01", "hello"); + c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + c.Add("k03", "hello3"); + c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + std::vector<std::string> keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kSnappyCompression; + c.Finish(options, &keys, &kvmap); + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/table/two_level_iterator.cc b/src/leveldb/table/two_level_iterator.cc new file mode 100644 index 0000000000..7822ebab9c --- /dev/null +++ b/src/leveldb/table/two_level_iterator.cc @@ -0,0 +1,182 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/two_level_iterator.h" + +#include "leveldb/table.h" +#include "table/block.h" +#include "table/format.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { + +typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&); + +class TwoLevelIterator: public Iterator { + public: + TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options); + + virtual ~TwoLevelIterator(); + + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + virtual void Next(); + virtual void Prev(); + + virtual bool Valid() const { + return data_iter_.Valid(); + } + virtual Slice key() const { + assert(Valid()); + return data_iter_.key(); + } + virtual Slice value() const { + assert(Valid()); + return data_iter_.value(); + } + virtual Status status() const { + // It'd be nice if status() returned a const Status& instead of a Status + if (!index_iter_.status().ok()) { + return index_iter_.status(); + } else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) { + return data_iter_.status(); + } else { + return status_; + } + } + + private: + void SaveError(const Status& s) { + if (status_.ok() && !s.ok()) status_ = s; + } + void SkipEmptyDataBlocksForward(); + void SkipEmptyDataBlocksBackward(); + void SetDataIterator(Iterator* data_iter); + void InitDataBlock(); + + BlockFunction block_function_; + void* arg_; + const ReadOptions options_; + Status status_; + IteratorWrapper index_iter_; + IteratorWrapper data_iter_; // May be NULL + // If data_iter_ is non-NULL, then "data_block_handle_" holds the + // "index_value" passed to block_function_ to create the data_iter_. + std::string data_block_handle_; +}; + +TwoLevelIterator::TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) + : block_function_(block_function), + arg_(arg), + options_(options), + index_iter_(index_iter), + data_iter_(NULL) { +} + +TwoLevelIterator::~TwoLevelIterator() { +} + +void TwoLevelIterator::Seek(const Slice& target) { + index_iter_.Seek(target); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.Seek(target); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToFirst() { + index_iter_.SeekToFirst(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToLast() { + index_iter_.SeekToLast(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + SkipEmptyDataBlocksBackward(); +} + +void TwoLevelIterator::Next() { + assert(Valid()); + data_iter_.Next(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::Prev() { + assert(Valid()); + data_iter_.Prev(); + SkipEmptyDataBlocksBackward(); +} + + +void TwoLevelIterator::SkipEmptyDataBlocksForward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Next(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + } +} + +void TwoLevelIterator::SkipEmptyDataBlocksBackward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Prev(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + } +} + +void TwoLevelIterator::SetDataIterator(Iterator* data_iter) { + if (data_iter_.iter() != NULL) SaveError(data_iter_.status()); + data_iter_.Set(data_iter); +} + +void TwoLevelIterator::InitDataBlock() { + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + } else { + Slice handle = index_iter_.value(); + if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) { + // data_iter_ is already constructed with this iterator, so + // no need to change anything + } else { + Iterator* iter = (*block_function_)(arg_, options_, handle); + data_block_handle_.assign(handle.data(), handle.size()); + SetDataIterator(iter); + } + } +} + +} // namespace + +Iterator* NewTwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) { + return new TwoLevelIterator(index_iter, block_function, arg, options); +} + +} // namespace leveldb diff --git a/src/leveldb/table/two_level_iterator.h b/src/leveldb/table/two_level_iterator.h new file mode 100644 index 0000000000..629ca34525 --- /dev/null +++ b/src/leveldb/table/two_level_iterator.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ +#define STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ + +#include "leveldb/iterator.h" + +namespace leveldb { + +struct ReadOptions; + +// Return a new two level iterator. A two-level iterator contains an +// index iterator whose values point to a sequence of blocks where +// each block is itself a sequence of key,value pairs. The returned +// two-level iterator yields the concatenation of all key/value pairs +// in the sequence of blocks. Takes ownership of "index_iter" and +// will delete it when no longer needed. +// +// Uses a supplied function to convert an index_iter value into +// an iterator over the contents of the corresponding block. +extern Iterator* NewTwoLevelIterator( + Iterator* index_iter, + Iterator* (*block_function)( + void* arg, + const ReadOptions& options, + const Slice& index_value), + void* arg, + const ReadOptions& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ diff --git a/src/leveldb/util/arena.cc b/src/leveldb/util/arena.cc new file mode 100644 index 0000000000..9551d6a3a2 --- /dev/null +++ b/src/leveldb/util/arena.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" +#include <assert.h> + +namespace leveldb { + +static const int kBlockSize = 4096; + +Arena::Arena() { + blocks_memory_ = 0; + alloc_ptr_ = NULL; // First allocation will allocate a block + alloc_bytes_remaining_ = 0; +} + +Arena::~Arena() { + for (size_t i = 0; i < blocks_.size(); i++) { + delete[] blocks_[i]; + } +} + +char* Arena::AllocateFallback(size_t bytes) { + if (bytes > kBlockSize / 4) { + // Object is more than a quarter of our block size. Allocate it separately + // to avoid wasting too much space in leftover bytes. + char* result = AllocateNewBlock(bytes); + return result; + } + + // We waste the remaining space in the current block. + alloc_ptr_ = AllocateNewBlock(kBlockSize); + alloc_bytes_remaining_ = kBlockSize; + + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; +} + +char* Arena::AllocateAligned(size_t bytes) { + const int align = sizeof(void*); // We'll align to pointer size + assert((align & (align-1)) == 0); // Pointer size should be a power of 2 + size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align-1); + size_t slop = (current_mod == 0 ? 0 : align - current_mod); + size_t needed = bytes + slop; + char* result; + if (needed <= alloc_bytes_remaining_) { + result = alloc_ptr_ + slop; + alloc_ptr_ += needed; + alloc_bytes_remaining_ -= needed; + } else { + // AllocateFallback always returned aligned memory + result = AllocateFallback(bytes); + } + assert((reinterpret_cast<uintptr_t>(result) & (align-1)) == 0); + return result; +} + +char* Arena::AllocateNewBlock(size_t block_bytes) { + char* result = new char[block_bytes]; + blocks_memory_ += block_bytes; + blocks_.push_back(result); + return result; +} + +} // namespace leveldb diff --git a/src/leveldb/util/arena.h b/src/leveldb/util/arena.h new file mode 100644 index 0000000000..8f7dde226c --- /dev/null +++ b/src/leveldb/util/arena.h @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ +#define STORAGE_LEVELDB_UTIL_ARENA_H_ + +#include <cstddef> +#include <vector> +#include <assert.h> +#include <stdint.h> + +namespace leveldb { + +class Arena { + public: + Arena(); + ~Arena(); + + // Return a pointer to a newly allocated memory block of "bytes" bytes. + char* Allocate(size_t bytes); + + // Allocate memory with the normal alignment guarantees provided by malloc + char* AllocateAligned(size_t bytes); + + // Returns an estimate of the total memory usage of data allocated + // by the arena (including space allocated but not yet used for user + // allocations). + size_t MemoryUsage() const { + return blocks_memory_ + blocks_.capacity() * sizeof(char*); + } + + private: + char* AllocateFallback(size_t bytes); + char* AllocateNewBlock(size_t block_bytes); + + // Allocation state + char* alloc_ptr_; + size_t alloc_bytes_remaining_; + + // Array of new[] allocated memory blocks + std::vector<char*> blocks_; + + // Bytes of memory in blocks allocated so far + size_t blocks_memory_; + + // No copying allowed + Arena(const Arena&); + void operator=(const Arena&); +}; + +inline char* Arena::Allocate(size_t bytes) { + // The semantics of what to return are a bit messy if we allow + // 0-byte allocations, so we disallow them here (we don't need + // them for our internal use). + assert(bytes > 0); + if (bytes <= alloc_bytes_remaining_) { + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; + } + return AllocateFallback(bytes); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ARENA_H_ diff --git a/src/leveldb/util/arena_test.cc b/src/leveldb/util/arena_test.cc new file mode 100644 index 0000000000..63d1778034 --- /dev/null +++ b/src/leveldb/util/arena_test.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" + +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +class ArenaTest { }; + +TEST(ArenaTest, Empty) { + Arena arena; +} + +TEST(ArenaTest, Simple) { + std::vector<std::pair<size_t, char*> > allocated; + Arena arena; + const int N = 100000; + size_t bytes = 0; + Random rnd(301); + for (int i = 0; i < N; i++) { + size_t s; + if (i % (N / 10) == 0) { + s = i; + } else { + s = rnd.OneIn(4000) ? rnd.Uniform(6000) : + (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); + } + if (s == 0) { + // Our arena disallows size 0 allocations. + s = 1; + } + char* r; + if (rnd.OneIn(10)) { + r = arena.AllocateAligned(s); + } else { + r = arena.Allocate(s); + } + + for (int b = 0; b < s; b++) { + // Fill the "i"th allocation with a known bit pattern + r[b] = i % 256; + } + bytes += s; + allocated.push_back(std::make_pair(s, r)); + ASSERT_GE(arena.MemoryUsage(), bytes); + if (i > N/10) { + ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); + } + } + for (int i = 0; i < allocated.size(); i++) { + size_t num_bytes = allocated[i].first; + const char* p = allocated[i].second; + for (int b = 0; b < num_bytes; b++) { + // Check the "i"th allocation for the known bit pattern + ASSERT_EQ(int(p[b]) & 0xff, i % 256); + } + } +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/bloom.cc b/src/leveldb/util/bloom.cc new file mode 100644 index 0000000000..d7941cd21f --- /dev/null +++ b/src/leveldb/util/bloom.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +namespace { +static uint32_t BloomHash(const Slice& key) { + return Hash(key.data(), key.size(), 0xbc9f1d34); +} + +class BloomFilterPolicy : public FilterPolicy { + private: + size_t bits_per_key_; + size_t k_; + + public: + explicit BloomFilterPolicy(int bits_per_key) + : bits_per_key_(bits_per_key) { + // We intentionally round down to reduce probing cost a little bit + k_ = static_cast<size_t>(bits_per_key * 0.69); // 0.69 =~ ln(2) + if (k_ < 1) k_ = 1; + if (k_ > 30) k_ = 30; + } + + virtual const char* Name() const { + return "leveldb.BuiltinBloomFilter"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + // Compute bloom filter size (in both bits and bytes) + size_t bits = n * bits_per_key_; + + // For small n, we can see a very high false positive rate. Fix it + // by enforcing a minimum bloom filter length. + if (bits < 64) bits = 64; + + size_t bytes = (bits + 7) / 8; + bits = bytes * 8; + + const size_t init_size = dst->size(); + dst->resize(init_size + bytes, 0); + dst->push_back(static_cast<char>(k_)); // Remember # of probes in filter + char* array = &(*dst)[init_size]; + for (size_t i = 0; i < n; i++) { + // Use double-hashing to generate a sequence of hash values. + // See analysis in [Kirsch,Mitzenmacher 2006]. + uint32_t h = BloomHash(keys[i]); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k_; j++) { + const uint32_t bitpos = h % bits; + array[bitpos/8] |= (1 << (bitpos % 8)); + h += delta; + } + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const { + const size_t len = bloom_filter.size(); + if (len < 2) return false; + + const char* array = bloom_filter.data(); + const size_t bits = (len - 1) * 8; + + // Use the encoded k so that we can read filters generated by + // bloom filters created using different parameters. + const size_t k = array[len-1]; + if (k > 30) { + // Reserved for potentially new encodings for short bloom filters. + // Consider it a match. + return true; + } + + uint32_t h = BloomHash(key); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k; j++) { + const uint32_t bitpos = h % bits; + if ((array[bitpos/8] & (1 << (bitpos % 8))) == 0) return false; + h += delta; + } + return true; + } +}; +} + +const FilterPolicy* NewBloomFilterPolicy(int bits_per_key) { + return new BloomFilterPolicy(bits_per_key); +} + +} // namespace leveldb diff --git a/src/leveldb/util/bloom_test.cc b/src/leveldb/util/bloom_test.cc new file mode 100644 index 0000000000..4a6ea1b7c8 --- /dev/null +++ b/src/leveldb/util/bloom_test.cc @@ -0,0 +1,159 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kVerbose = 1; + +static Slice Key(int i, char* buffer) { + memcpy(buffer, &i, sizeof(i)); + return Slice(buffer, sizeof(i)); +} + +class BloomTest { + private: + const FilterPolicy* policy_; + std::string filter_; + std::vector<std::string> keys_; + + public: + BloomTest() : policy_(NewBloomFilterPolicy(10)) { } + + ~BloomTest() { + delete policy_; + } + + void Reset() { + keys_.clear(); + filter_.clear(); + } + + void Add(const Slice& s) { + keys_.push_back(s.ToString()); + } + + void Build() { + std::vector<Slice> key_slices; + for (size_t i = 0; i < keys_.size(); i++) { + key_slices.push_back(Slice(keys_[i])); + } + filter_.clear(); + policy_->CreateFilter(&key_slices[0], key_slices.size(), &filter_); + keys_.clear(); + if (kVerbose >= 2) DumpFilter(); + } + + size_t FilterSize() const { + return filter_.size(); + } + + void DumpFilter() { + fprintf(stderr, "F("); + for (size_t i = 0; i+1 < filter_.size(); i++) { + const unsigned int c = static_cast<unsigned int>(filter_[i]); + for (int j = 0; j < 8; j++) { + fprintf(stderr, "%c", (c & (1 <<j)) ? '1' : '.'); + } + } + fprintf(stderr, ")\n"); + } + + bool Matches(const Slice& s) { + if (!keys_.empty()) { + Build(); + } + return policy_->KeyMayMatch(s, filter_); + } + + double FalsePositiveRate() { + char buffer[sizeof(int)]; + int result = 0; + for (int i = 0; i < 10000; i++) { + if (Matches(Key(i + 1000000000, buffer))) { + result++; + } + } + return result / 10000.0; + } +}; + +TEST(BloomTest, EmptyFilter) { + ASSERT_TRUE(! Matches("hello")); + ASSERT_TRUE(! Matches("world")); +} + +TEST(BloomTest, Small) { + Add("hello"); + Add("world"); + ASSERT_TRUE(Matches("hello")); + ASSERT_TRUE(Matches("world")); + ASSERT_TRUE(! Matches("x")); + ASSERT_TRUE(! Matches("foo")); +} + +static int NextLength(int length) { + if (length < 10) { + length += 1; + } else if (length < 100) { + length += 10; + } else if (length < 1000) { + length += 100; + } else { + length += 1000; + } + return length; +} + +TEST(BloomTest, VaryingLengths) { + char buffer[sizeof(int)]; + + // Count number of filters that significantly exceed the false positive rate + int mediocre_filters = 0; + int good_filters = 0; + + for (int length = 1; length <= 10000; length = NextLength(length)) { + Reset(); + for (int i = 0; i < length; i++) { + Add(Key(i, buffer)); + } + Build(); + + ASSERT_LE(FilterSize(), (length * 10 / 8) + 40) << length; + + // All added keys must match + for (int i = 0; i < length; i++) { + ASSERT_TRUE(Matches(Key(i, buffer))) + << "Length " << length << "; key " << i; + } + + // Check false positive rate + double rate = FalsePositiveRate(); + if (kVerbose >= 1) { + fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n", + rate*100.0, length, static_cast<int>(FilterSize())); + } + ASSERT_LE(rate, 0.02); // Must not be over 2% + if (rate > 0.0125) mediocre_filters++; // Allowed, but not too often + else good_filters++; + } + if (kVerbose >= 1) { + fprintf(stderr, "Filters: %d good, %d mediocre\n", + good_filters, mediocre_filters); + } + ASSERT_LE(mediocre_filters, good_filters/5); +} + +// Different bits-per-byte + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/cache.cc b/src/leveldb/util/cache.cc new file mode 100644 index 0000000000..24f1f63f4f --- /dev/null +++ b/src/leveldb/util/cache.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "leveldb/cache.h" +#include "port/port.h" +#include "util/hash.h" +#include "util/mutexlock.h" + +namespace leveldb { + +Cache::~Cache() { +} + +namespace { + +// LRU cache implementation + +// An entry is a variable length heap-allocated structure. Entries +// are kept in a circular doubly linked list ordered by access time. +struct LRUHandle { + void* value; + void (*deleter)(const Slice&, void* value); + LRUHandle* next_hash; + LRUHandle* next; + LRUHandle* prev; + size_t charge; // TODO(opt): Only allow uint32_t? + size_t key_length; + uint32_t refs; + uint32_t hash; // Hash of key(); used for fast sharding and comparisons + char key_data[1]; // Beginning of key + + Slice key() const { + // For cheaper lookups, we allow a temporary Handle object + // to store a pointer to a key in "value". + if (next == this) { + return *(reinterpret_cast<Slice*>(value)); + } else { + return Slice(key_data, key_length); + } + } +}; + +// We provide our own simple hash table since it removes a whole bunch +// of porting hacks and is also faster than some of the built-in hash +// table implementations in some of the compiler/runtime combinations +// we have tested. E.g., readrandom speeds up by ~5% over the g++ +// 4.4.3's builtin hashtable. +class HandleTable { + public: + HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); } + ~HandleTable() { delete[] list_; } + + LRUHandle* Lookup(const Slice& key, uint32_t hash) { + return *FindPointer(key, hash); + } + + LRUHandle* Insert(LRUHandle* h) { + LRUHandle** ptr = FindPointer(h->key(), h->hash); + LRUHandle* old = *ptr; + h->next_hash = (old == NULL ? NULL : old->next_hash); + *ptr = h; + if (old == NULL) { + ++elems_; + if (elems_ > length_) { + // Since each cache entry is fairly large, we aim for a small + // average linked list length (<= 1). + Resize(); + } + } + return old; + } + + LRUHandle* Remove(const Slice& key, uint32_t hash) { + LRUHandle** ptr = FindPointer(key, hash); + LRUHandle* result = *ptr; + if (result != NULL) { + *ptr = result->next_hash; + --elems_; + } + return result; + } + + private: + // The table consists of an array of buckets where each bucket is + // a linked list of cache entries that hash into the bucket. + uint32_t length_; + uint32_t elems_; + LRUHandle** list_; + + // Return a pointer to slot that points to a cache entry that + // matches key/hash. If there is no such cache entry, return a + // pointer to the trailing slot in the corresponding linked list. + LRUHandle** FindPointer(const Slice& key, uint32_t hash) { + LRUHandle** ptr = &list_[hash & (length_ - 1)]; + while (*ptr != NULL && + ((*ptr)->hash != hash || key != (*ptr)->key())) { + ptr = &(*ptr)->next_hash; + } + return ptr; + } + + void Resize() { + uint32_t new_length = 4; + while (new_length < elems_) { + new_length *= 2; + } + LRUHandle** new_list = new LRUHandle*[new_length]; + memset(new_list, 0, sizeof(new_list[0]) * new_length); + uint32_t count = 0; + for (uint32_t i = 0; i < length_; i++) { + LRUHandle* h = list_[i]; + while (h != NULL) { + LRUHandle* next = h->next_hash; + Slice key = h->key(); + uint32_t hash = h->hash; + LRUHandle** ptr = &new_list[hash & (new_length - 1)]; + h->next_hash = *ptr; + *ptr = h; + h = next; + count++; + } + } + assert(elems_ == count); + delete[] list_; + list_ = new_list; + length_ = new_length; + } +}; + +// A single shard of sharded cache. +class LRUCache { + public: + LRUCache(); + ~LRUCache(); + + // Separate from constructor so caller can easily make an array of LRUCache + void SetCapacity(size_t capacity) { capacity_ = capacity; } + + // Like Cache methods, but with an extra "hash" parameter. + Cache::Handle* Insert(const Slice& key, uint32_t hash, + void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)); + Cache::Handle* Lookup(const Slice& key, uint32_t hash); + void Release(Cache::Handle* handle); + void Erase(const Slice& key, uint32_t hash); + + private: + void LRU_Remove(LRUHandle* e); + void LRU_Append(LRUHandle* e); + void Unref(LRUHandle* e); + + // Initialized before use. + size_t capacity_; + + // mutex_ protects the following state. + port::Mutex mutex_; + size_t usage_; + uint64_t last_id_; + + // Dummy head of LRU list. + // lru.prev is newest entry, lru.next is oldest entry. + LRUHandle lru_; + + HandleTable table_; +}; + +LRUCache::LRUCache() + : usage_(0), + last_id_(0) { + // Make empty circular linked list + lru_.next = &lru_; + lru_.prev = &lru_; +} + +LRUCache::~LRUCache() { + for (LRUHandle* e = lru_.next; e != &lru_; ) { + LRUHandle* next = e->next; + assert(e->refs == 1); // Error if caller has an unreleased handle + Unref(e); + e = next; + } +} + +void LRUCache::Unref(LRUHandle* e) { + assert(e->refs > 0); + e->refs--; + if (e->refs <= 0) { + usage_ -= e->charge; + (*e->deleter)(e->key(), e->value); + free(e); + } +} + +void LRUCache::LRU_Remove(LRUHandle* e) { + e->next->prev = e->prev; + e->prev->next = e->next; +} + +void LRUCache::LRU_Append(LRUHandle* e) { + // Make "e" newest entry by inserting just before lru_ + e->next = &lru_; + e->prev = lru_.prev; + e->prev->next = e; + e->next->prev = e; +} + +Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + LRUHandle* e = table_.Lookup(key, hash); + if (e != NULL) { + e->refs++; + LRU_Remove(e); + LRU_Append(e); + } + return reinterpret_cast<Cache::Handle*>(e); +} + +void LRUCache::Release(Cache::Handle* handle) { + MutexLock l(&mutex_); + Unref(reinterpret_cast<LRUHandle*>(handle)); +} + +Cache::Handle* LRUCache::Insert( + const Slice& key, uint32_t hash, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + MutexLock l(&mutex_); + + LRUHandle* e = reinterpret_cast<LRUHandle*>( + malloc(sizeof(LRUHandle)-1 + key.size())); + e->value = value; + e->deleter = deleter; + e->charge = charge; + e->key_length = key.size(); + e->hash = hash; + e->refs = 2; // One from LRUCache, one for the returned handle + memcpy(e->key_data, key.data(), key.size()); + LRU_Append(e); + usage_ += charge; + + LRUHandle* old = table_.Insert(e); + if (old != NULL) { + LRU_Remove(old); + Unref(old); + } + + while (usage_ > capacity_ && lru_.next != &lru_) { + LRUHandle* old = lru_.next; + LRU_Remove(old); + table_.Remove(old->key(), old->hash); + Unref(old); + } + + return reinterpret_cast<Cache::Handle*>(e); +} + +void LRUCache::Erase(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + LRUHandle* e = table_.Remove(key, hash); + if (e != NULL) { + LRU_Remove(e); + Unref(e); + } +} + +static const int kNumShardBits = 4; +static const int kNumShards = 1 << kNumShardBits; + +class ShardedLRUCache : public Cache { + private: + LRUCache shard_[kNumShards]; + port::Mutex id_mutex_; + uint64_t last_id_; + + static inline uint32_t HashSlice(const Slice& s) { + return Hash(s.data(), s.size(), 0); + } + + static uint32_t Shard(uint32_t hash) { + return hash >> (32 - kNumShardBits); + } + + public: + explicit ShardedLRUCache(size_t capacity) + : last_id_(0) { + const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards; + for (int s = 0; s < kNumShards; s++) { + shard_[s].SetCapacity(per_shard); + } + } + virtual ~ShardedLRUCache() { } + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); + } + virtual Handle* Lookup(const Slice& key) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Lookup(key, hash); + } + virtual void Release(Handle* handle) { + LRUHandle* h = reinterpret_cast<LRUHandle*>(handle); + shard_[Shard(h->hash)].Release(handle); + } + virtual void Erase(const Slice& key) { + const uint32_t hash = HashSlice(key); + shard_[Shard(hash)].Erase(key, hash); + } + virtual void* Value(Handle* handle) { + return reinterpret_cast<LRUHandle*>(handle)->value; + } + virtual uint64_t NewId() { + MutexLock l(&id_mutex_); + return ++(last_id_); + } +}; + +} // end anonymous namespace + +Cache* NewLRUCache(size_t capacity) { + return new ShardedLRUCache(capacity); +} + +} // namespace leveldb diff --git a/src/leveldb/util/cache_test.cc b/src/leveldb/util/cache_test.cc new file mode 100644 index 0000000000..43716715a8 --- /dev/null +++ b/src/leveldb/util/cache_test.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/cache.h" + +#include <vector> +#include "util/coding.h" +#include "util/testharness.h" + +namespace leveldb { + +// Conversions between numeric keys/values and the types expected by Cache. +static std::string EncodeKey(int k) { + std::string result; + PutFixed32(&result, k); + return result; +} +static int DecodeKey(const Slice& k) { + assert(k.size() == 4); + return DecodeFixed32(k.data()); +} +static void* EncodeValue(uintptr_t v) { return reinterpret_cast<void*>(v); } +static int DecodeValue(void* v) { return reinterpret_cast<uintptr_t>(v); } + +class CacheTest { + public: + static CacheTest* current_; + + static void Deleter(const Slice& key, void* v) { + current_->deleted_keys_.push_back(DecodeKey(key)); + current_->deleted_values_.push_back(DecodeValue(v)); + } + + static const int kCacheSize = 1000; + std::vector<int> deleted_keys_; + std::vector<int> deleted_values_; + Cache* cache_; + + CacheTest() : cache_(NewLRUCache(kCacheSize)) { + current_ = this; + } + + ~CacheTest() { + delete cache_; + } + + int Lookup(int key) { + Cache::Handle* handle = cache_->Lookup(EncodeKey(key)); + const int r = (handle == NULL) ? -1 : DecodeValue(cache_->Value(handle)); + if (handle != NULL) { + cache_->Release(handle); + } + return r; + } + + void Insert(int key, int value, int charge = 1) { + cache_->Release(cache_->Insert(EncodeKey(key), EncodeValue(value), charge, + &CacheTest::Deleter)); + } + + void Erase(int key) { + cache_->Erase(EncodeKey(key)); + } +}; +CacheTest* CacheTest::current_; + +TEST(CacheTest, HitAndMiss) { + ASSERT_EQ(-1, Lookup(100)); + + Insert(100, 101); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(200, 201); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(100, 102); + ASSERT_EQ(102, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); +} + +TEST(CacheTest, Erase) { + Erase(200); + ASSERT_EQ(0, deleted_keys_.size()); + + Insert(100, 101); + Insert(200, 201); + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); +} + +TEST(CacheTest, EntriesArePinned) { + Insert(100, 101); + Cache::Handle* h1 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(101, DecodeValue(cache_->Value(h1))); + + Insert(100, 102); + Cache::Handle* h2 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(102, DecodeValue(cache_->Value(h2))); + ASSERT_EQ(0, deleted_keys_.size()); + + cache_->Release(h1); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(1, deleted_keys_.size()); + + cache_->Release(h2); + ASSERT_EQ(2, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[1]); + ASSERT_EQ(102, deleted_values_[1]); +} + +TEST(CacheTest, EvictionPolicy) { + Insert(100, 101); + Insert(200, 201); + + // Frequently used entry must be kept around + for (int i = 0; i < kCacheSize + 100; i++) { + Insert(1000+i, 2000+i); + ASSERT_EQ(2000+i, Lookup(1000+i)); + ASSERT_EQ(101, Lookup(100)); + } + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); +} + +TEST(CacheTest, HeavyEntries) { + // Add a bunch of light and heavy entries and then count the combined + // size of items still in the cache, which must be approximately the + // same as the total capacity. + const int kLight = 1; + const int kHeavy = 10; + int added = 0; + int index = 0; + while (added < 2*kCacheSize) { + const int weight = (index & 1) ? kLight : kHeavy; + Insert(index, 1000+index, weight); + added += weight; + index++; + } + + int cached_weight = 0; + for (int i = 0; i < index; i++) { + const int weight = (i & 1 ? kLight : kHeavy); + int r = Lookup(i); + if (r >= 0) { + cached_weight += weight; + ASSERT_EQ(1000+i, r); + } + } + ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10); +} + +TEST(CacheTest, NewId) { + uint64_t a = cache_->NewId(); + uint64_t b = cache_->NewId(); + ASSERT_NE(a, b); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/coding.cc b/src/leveldb/util/coding.cc new file mode 100644 index 0000000000..dbd7a6545c --- /dev/null +++ b/src/leveldb/util/coding.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +namespace leveldb { + +void EncodeFixed32(char* buf, uint32_t value) { +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(buf, &value, sizeof(value)); +#else + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; +#endif +} + +void EncodeFixed64(char* buf, uint64_t value) { +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(buf, &value, sizeof(value)); +#else + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; + buf[4] = (value >> 32) & 0xff; + buf[5] = (value >> 40) & 0xff; + buf[6] = (value >> 48) & 0xff; + buf[7] = (value >> 56) & 0xff; +#endif +} + +void PutFixed32(std::string* dst, uint32_t value) { + char buf[sizeof(value)]; + EncodeFixed32(buf, value); + dst->append(buf, sizeof(buf)); +} + +void PutFixed64(std::string* dst, uint64_t value) { + char buf[sizeof(value)]; + EncodeFixed64(buf, value); + dst->append(buf, sizeof(buf)); +} + +char* EncodeVarint32(char* dst, uint32_t v) { + // Operate on characters as unsigneds + unsigned char* ptr = reinterpret_cast<unsigned char*>(dst); + static const int B = 128; + if (v < (1<<7)) { + *(ptr++) = v; + } else if (v < (1<<14)) { + *(ptr++) = v | B; + *(ptr++) = v>>7; + } else if (v < (1<<21)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = v>>14; + } else if (v < (1<<28)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = v>>21; + } else { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = (v>>21) | B; + *(ptr++) = v>>28; + } + return reinterpret_cast<char*>(ptr); +} + +void PutVarint32(std::string* dst, uint32_t v) { + char buf[5]; + char* ptr = EncodeVarint32(buf, v); + dst->append(buf, ptr - buf); +} + +char* EncodeVarint64(char* dst, uint64_t v) { + static const int B = 128; + unsigned char* ptr = reinterpret_cast<unsigned char*>(dst); + while (v >= B) { + *(ptr++) = (v & (B-1)) | B; + v >>= 7; + } + *(ptr++) = static_cast<unsigned char>(v); + return reinterpret_cast<char*>(ptr); +} + +void PutVarint64(std::string* dst, uint64_t v) { + char buf[10]; + char* ptr = EncodeVarint64(buf, v); + dst->append(buf, ptr - buf); +} + +void PutLengthPrefixedSlice(std::string* dst, const Slice& value) { + PutVarint32(dst, value.size()); + dst->append(value.data(), value.size()); +} + +int VarintLength(uint64_t v) { + int len = 1; + while (v >= 128) { + v >>= 7; + len++; + } + return len; +} + +const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value) { + uint32_t result = 0; + for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) { + uint32_t byte = *(reinterpret_cast<const unsigned char*>(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast<const char*>(p); + } + } + return NULL; +} + +bool GetVarint32(Slice* input, uint32_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint32Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) { + uint64_t result = 0; + for (uint32_t shift = 0; shift <= 63 && p < limit; shift += 7) { + uint64_t byte = *(reinterpret_cast<const unsigned char*>(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast<const char*>(p); + } + } + return NULL; +} + +bool GetVarint64(Slice* input, uint64_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint64Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetLengthPrefixedSlice(const char* p, const char* limit, + Slice* result) { + uint32_t len; + p = GetVarint32Ptr(p, limit, &len); + if (p == NULL) return NULL; + if (p + len > limit) return NULL; + *result = Slice(p, len); + return p + len; +} + +bool GetLengthPrefixedSlice(Slice* input, Slice* result) { + uint32_t len; + if (GetVarint32(input, &len) && + input->size() >= len) { + *result = Slice(input->data(), len); + input->remove_prefix(len); + return true; + } else { + return false; + } +} + +} // namespace leveldb diff --git a/src/leveldb/util/coding.h b/src/leveldb/util/coding.h new file mode 100644 index 0000000000..3993c4a755 --- /dev/null +++ b/src/leveldb/util/coding.h @@ -0,0 +1,104 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Endian-neutral encoding: +// * Fixed-length numbers are encoded with least-significant byte first +// * In addition we support variable length "varint" encoding +// * Strings are encoded prefixed by their length in varint format + +#ifndef STORAGE_LEVELDB_UTIL_CODING_H_ +#define STORAGE_LEVELDB_UTIL_CODING_H_ + +#include <stdint.h> +#include <string.h> +#include <string> +#include "leveldb/slice.h" +#include "port/port.h" + +namespace leveldb { + +// Standard Put... routines append to a string +extern void PutFixed32(std::string* dst, uint32_t value); +extern void PutFixed64(std::string* dst, uint64_t value); +extern void PutVarint32(std::string* dst, uint32_t value); +extern void PutVarint64(std::string* dst, uint64_t value); +extern void PutLengthPrefixedSlice(std::string* dst, const Slice& value); + +// Standard Get... routines parse a value from the beginning of a Slice +// and advance the slice past the parsed value. +extern bool GetVarint32(Slice* input, uint32_t* value); +extern bool GetVarint64(Slice* input, uint64_t* value); +extern bool GetLengthPrefixedSlice(Slice* input, Slice* result); + +// Pointer-based variants of GetVarint... These either store a value +// in *v and return a pointer just past the parsed value, or return +// NULL on error. These routines only look at bytes in the range +// [p..limit-1] +extern const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v); +extern const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v); + +// Returns the length of the varint32 or varint64 encoding of "v" +extern int VarintLength(uint64_t v); + +// Lower-level versions of Put... that write directly into a character buffer +// REQUIRES: dst has enough space for the value being written +extern void EncodeFixed32(char* dst, uint32_t value); +extern void EncodeFixed64(char* dst, uint64_t value); + +// Lower-level versions of Put... that write directly into a character buffer +// and return a pointer just past the last byte written. +// REQUIRES: dst has enough space for the value being written +extern char* EncodeVarint32(char* dst, uint32_t value); +extern char* EncodeVarint64(char* dst, uint64_t value); + +// Lower-level versions of Get... that read directly from a character buffer +// without any bounds checking. + +inline uint32_t DecodeFixed32(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint32_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + return ((static_cast<uint32_t>(static_cast<unsigned char>(ptr[0]))) + | (static_cast<uint32_t>(static_cast<unsigned char>(ptr[1])) << 8) + | (static_cast<uint32_t>(static_cast<unsigned char>(ptr[2])) << 16) + | (static_cast<uint32_t>(static_cast<unsigned char>(ptr[3])) << 24)); + } +} + +inline uint64_t DecodeFixed64(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint64_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + uint64_t lo = DecodeFixed32(ptr); + uint64_t hi = DecodeFixed32(ptr + 4); + return (hi << 32) | lo; + } +} + +// Internal routine for use by fallback path of GetVarint32Ptr +extern const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value); +inline const char* GetVarint32Ptr(const char* p, + const char* limit, + uint32_t* value) { + if (p < limit) { + uint32_t result = *(reinterpret_cast<const unsigned char*>(p)); + if ((result & 128) == 0) { + *value = result; + return p + 1; + } + } + return GetVarint32PtrFallback(p, limit, value); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CODING_H_ diff --git a/src/leveldb/util/coding_test.cc b/src/leveldb/util/coding_test.cc new file mode 100644 index 0000000000..2c52b17b60 --- /dev/null +++ b/src/leveldb/util/coding_test.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +#include "util/testharness.h" + +namespace leveldb { + +class Coding { }; + +TEST(Coding, Fixed32) { + std::string s; + for (uint32_t v = 0; v < 100000; v++) { + PutFixed32(&s, v); + } + + const char* p = s.data(); + for (uint32_t v = 0; v < 100000; v++) { + uint32_t actual = DecodeFixed32(p); + ASSERT_EQ(v, actual); + p += sizeof(uint32_t); + } +} + +TEST(Coding, Fixed64) { + std::string s; + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast<uint64_t>(1) << power; + PutFixed64(&s, v - 1); + PutFixed64(&s, v + 0); + PutFixed64(&s, v + 1); + } + + const char* p = s.data(); + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast<uint64_t>(1) << power; + uint64_t actual; + actual = DecodeFixed64(p); + ASSERT_EQ(v-1, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+0, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+1, actual); + p += sizeof(uint64_t); + } +} + +// Test that encoding routines generate little-endian encodings +TEST(Coding, EncodingOutput) { + std::string dst; + PutFixed32(&dst, 0x04030201); + ASSERT_EQ(4, dst.size()); + ASSERT_EQ(0x01, static_cast<int>(dst[0])); + ASSERT_EQ(0x02, static_cast<int>(dst[1])); + ASSERT_EQ(0x03, static_cast<int>(dst[2])); + ASSERT_EQ(0x04, static_cast<int>(dst[3])); + + dst.clear(); + PutFixed64(&dst, 0x0807060504030201ull); + ASSERT_EQ(8, dst.size()); + ASSERT_EQ(0x01, static_cast<int>(dst[0])); + ASSERT_EQ(0x02, static_cast<int>(dst[1])); + ASSERT_EQ(0x03, static_cast<int>(dst[2])); + ASSERT_EQ(0x04, static_cast<int>(dst[3])); + ASSERT_EQ(0x05, static_cast<int>(dst[4])); + ASSERT_EQ(0x06, static_cast<int>(dst[5])); + ASSERT_EQ(0x07, static_cast<int>(dst[6])); + ASSERT_EQ(0x08, static_cast<int>(dst[7])); +} + +TEST(Coding, Varint32) { + std::string s; + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t v = (i / 32) << (i % 32); + PutVarint32(&s, v); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t expected = (i / 32) << (i % 32); + uint32_t actual; + const char* start = p; + p = GetVarint32Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(expected, actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, s.data() + s.size()); +} + +TEST(Coding, Varint64) { + // Construct the list of values to check + std::vector<uint64_t> values; + // Some special values + values.push_back(0); + values.push_back(100); + values.push_back(~static_cast<uint64_t>(0)); + values.push_back(~static_cast<uint64_t>(0) - 1); + for (uint32_t k = 0; k < 64; k++) { + // Test values near powers of two + const uint64_t power = 1ull << k; + values.push_back(power); + values.push_back(power-1); + values.push_back(power+1); + }; + + std::string s; + for (int i = 0; i < values.size(); i++) { + PutVarint64(&s, values[i]); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (int i = 0; i < values.size(); i++) { + ASSERT_TRUE(p < limit); + uint64_t actual; + const char* start = p; + p = GetVarint64Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(values[i], actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, limit); + +} + +TEST(Coding, Varint32Overflow) { + uint32_t result; + std::string input("\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint32Truncation) { + uint32_t large_value = (1u << 31) + 100; + std::string s; + PutVarint32(&s, large_value); + uint32_t result; + for (int len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Varint64Overflow) { + uint64_t result; + std::string input("\x81\x82\x83\x84\x85\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint64Truncation) { + uint64_t large_value = (1ull << 63) + 100ull; + std::string s; + PutVarint64(&s, large_value); + uint64_t result; + for (int len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Strings) { + std::string s; + PutLengthPrefixedSlice(&s, Slice("")); + PutLengthPrefixedSlice(&s, Slice("foo")); + PutLengthPrefixedSlice(&s, Slice("bar")); + PutLengthPrefixedSlice(&s, Slice(std::string(200, 'x'))); + + Slice input(s); + Slice v; + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("foo", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("bar", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ(std::string(200, 'x'), v.ToString()); + ASSERT_EQ("", input.ToString()); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/comparator.cc b/src/leveldb/util/comparator.cc new file mode 100644 index 0000000000..4b7b5724ef --- /dev/null +++ b/src/leveldb/util/comparator.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <algorithm> +#include <stdint.h> +#include "leveldb/comparator.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" + +namespace leveldb { + +Comparator::~Comparator() { } + +namespace { +class BytewiseComparatorImpl : public Comparator { + public: + BytewiseComparatorImpl() { } + + virtual const char* Name() const { + return "leveldb.BytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return a.compare(b); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Find length of common prefix + size_t min_length = std::min(start->size(), limit.size()); + size_t diff_index = 0; + while ((diff_index < min_length) && + ((*start)[diff_index] == limit[diff_index])) { + diff_index++; + } + + if (diff_index >= min_length) { + // Do not shorten if one string is a prefix of the other + } else { + uint8_t diff_byte = static_cast<uint8_t>((*start)[diff_index]); + if (diff_byte < static_cast<uint8_t>(0xff) && + diff_byte + 1 < static_cast<uint8_t>(limit[diff_index])) { + (*start)[diff_index]++; + start->resize(diff_index + 1); + assert(Compare(*start, limit) < 0); + } + } + } + + virtual void FindShortSuccessor(std::string* key) const { + // Find first character that can be incremented + size_t n = key->size(); + for (size_t i = 0; i < n; i++) { + const uint8_t byte = (*key)[i]; + if (byte != static_cast<uint8_t>(0xff)) { + (*key)[i] = byte + 1; + key->resize(i+1); + return; + } + } + // *key is a run of 0xffs. Leave it alone. + } +}; +} // namespace + +static port::OnceType once = LEVELDB_ONCE_INIT; +static const Comparator* bytewise; + +static void InitModule() { + bytewise = new BytewiseComparatorImpl; +} + +const Comparator* BytewiseComparator() { + port::InitOnce(&once, InitModule); + return bytewise; +} + +} // namespace leveldb diff --git a/src/leveldb/util/crc32c.cc b/src/leveldb/util/crc32c.cc new file mode 100644 index 0000000000..6db9e77077 --- /dev/null +++ b/src/leveldb/util/crc32c.cc @@ -0,0 +1,332 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A portable implementation of crc32c, optimized to handle +// four bytes at a time. + +#include "util/crc32c.h" + +#include <stdint.h> +#include "util/coding.h" + +namespace leveldb { +namespace crc32c { + +static const uint32_t table0_[256] = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, + 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, + 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, + 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, + 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, + 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, + 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, + 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, + 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, + 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, + 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, + 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, + 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, + 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, + 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, + 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, + 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, + 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, + 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, + 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, + 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, + 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, + 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, + 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, + 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, + 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, + 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, + 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, + 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, + 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, + 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, + 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, + 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, + 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, + 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, + 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, + 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, + 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, + 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, + 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, + 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, + 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, + 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 +}; +static const uint32_t table1_[256] = { + 0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899, + 0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945, + 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21, + 0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, + 0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918, + 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4, + 0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, + 0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c, + 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b, + 0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47, + 0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823, + 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff, + 0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, + 0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6, + 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2, + 0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, + 0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d, + 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41, + 0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, + 0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9, + 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c, + 0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, + 0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4, + 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78, + 0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, + 0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43, + 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27, + 0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, + 0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e, + 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2, + 0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6, + 0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a, + 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260, + 0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, + 0x66d73941, 0x7575a136, 0x419209af, 0x523091d8, + 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004, + 0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, + 0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d, + 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059, + 0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185, + 0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162, + 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be, + 0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da, + 0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306, + 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3, + 0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, + 0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b, + 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287, + 0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, + 0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8, + 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc, + 0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, + 0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5, + 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439, + 0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, + 0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781, + 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766, + 0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba, + 0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de, + 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502, + 0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, + 0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b, + 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f, + 0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483 +}; +static const uint32_t table2_[256] = { + 0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073, + 0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469, + 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6, + 0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac, + 0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9, + 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3, + 0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c, + 0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726, + 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67, + 0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, + 0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2, + 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8, + 0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, + 0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7, + 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828, + 0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, + 0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa, + 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0, + 0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, + 0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75, + 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20, + 0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, + 0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5, + 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff, + 0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, + 0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4, + 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b, + 0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, + 0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634, + 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e, + 0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, + 0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb, + 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730, + 0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, + 0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5, + 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def, + 0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, + 0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0, + 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f, + 0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, + 0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24, + 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e, + 0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, + 0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb, + 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae, + 0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, + 0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b, + 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71, + 0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9, + 0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3, + 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c, + 0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36, + 0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63, + 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79, + 0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, + 0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc, + 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd, + 0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, + 0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238, + 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622, + 0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, + 0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d, + 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2, + 0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8 +}; +static const uint32_t table3_[256] = { + 0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939, + 0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca, + 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf, + 0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, + 0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804, + 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7, + 0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, + 0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11, + 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2, + 0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, + 0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54, + 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7, + 0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, + 0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c, + 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69, + 0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a, + 0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de, + 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d, + 0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, + 0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb, + 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3, + 0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, + 0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405, + 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6, + 0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, + 0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6, + 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3, + 0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, + 0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368, + 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b, + 0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e, + 0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d, + 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006, + 0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, + 0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0, + 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213, + 0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b, + 0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8, + 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd, + 0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, + 0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d, + 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e, + 0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, + 0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698, + 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0, + 0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, + 0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656, + 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5, + 0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, + 0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12, + 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07, + 0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, + 0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc, + 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f, + 0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, + 0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9, + 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a, + 0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, + 0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c, + 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f, + 0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, + 0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4, + 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1, + 0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842 +}; + +// Used to fetch a naturally-aligned 32-bit word in little endian byte-order +static inline uint32_t LE_LOAD32(const uint8_t *p) { + return DecodeFixed32(reinterpret_cast<const char*>(p)); +} + +uint32_t Extend(uint32_t crc, const char* buf, size_t size) { + const uint8_t *p = reinterpret_cast<const uint8_t *>(buf); + const uint8_t *e = p + size; + uint32_t l = crc ^ 0xffffffffu; + +#define STEP1 do { \ + int c = (l & 0xff) ^ *p++; \ + l = table0_[c] ^ (l >> 8); \ +} while (0) +#define STEP4 do { \ + uint32_t c = l ^ LE_LOAD32(p); \ + p += 4; \ + l = table3_[c & 0xff] ^ \ + table2_[(c >> 8) & 0xff] ^ \ + table1_[(c >> 16) & 0xff] ^ \ + table0_[c >> 24]; \ +} while (0) + + // Point x at first 4-byte aligned byte in string. This might be + // just past the end of the string. + const uintptr_t pval = reinterpret_cast<uintptr_t>(p); + const uint8_t* x = reinterpret_cast<const uint8_t*>(((pval + 3) >> 2) << 2); + if (x <= e) { + // Process bytes until finished or p is 4-byte aligned + while (p != x) { + STEP1; + } + } + // Process bytes 16 at a time + while ((e-p) >= 16) { + STEP4; STEP4; STEP4; STEP4; + } + // Process bytes 4 at a time + while ((e-p) >= 4) { + STEP4; + } + // Process the last few bytes + while (p != e) { + STEP1; + } +#undef STEP4 +#undef STEP1 + return l ^ 0xffffffffu; +} + +} // namespace crc32c +} // namespace leveldb diff --git a/src/leveldb/util/crc32c.h b/src/leveldb/util/crc32c.h new file mode 100644 index 0000000000..1d7e5c075d --- /dev/null +++ b/src/leveldb/util/crc32c.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_CRC32C_H_ +#define STORAGE_LEVELDB_UTIL_CRC32C_H_ + +#include <stddef.h> +#include <stdint.h> + +namespace leveldb { +namespace crc32c { + +// Return the crc32c of concat(A, data[0,n-1]) where init_crc is the +// crc32c of some string A. Extend() is often used to maintain the +// crc32c of a stream of data. +extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n); + +// Return the crc32c of data[0,n-1] +inline uint32_t Value(const char* data, size_t n) { + return Extend(0, data, n); +} + +static const uint32_t kMaskDelta = 0xa282ead8ul; + +// Return a masked representation of crc. +// +// Motivation: it is problematic to compute the CRC of a string that +// contains embedded CRCs. Therefore we recommend that CRCs stored +// somewhere (e.g., in files) should be masked before being stored. +inline uint32_t Mask(uint32_t crc) { + // Rotate right by 15 bits and add a constant. + return ((crc >> 15) | (crc << 17)) + kMaskDelta; +} + +// Return the crc whose masked representation is masked_crc. +inline uint32_t Unmask(uint32_t masked_crc) { + uint32_t rot = masked_crc - kMaskDelta; + return ((rot >> 17) | (rot << 15)); +} + +} // namespace crc32c +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CRC32C_H_ diff --git a/src/leveldb/util/crc32c_test.cc b/src/leveldb/util/crc32c_test.cc new file mode 100644 index 0000000000..4b957ee120 --- /dev/null +++ b/src/leveldb/util/crc32c_test.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/crc32c.h" +#include "util/testharness.h" + +namespace leveldb { +namespace crc32c { + +class CRC { }; + +TEST(CRC, StandardResults) { + // From rfc3720 section B.4. + char buf[32]; + + memset(buf, 0, sizeof(buf)); + ASSERT_EQ(0x8a9136aa, Value(buf, sizeof(buf))); + + memset(buf, 0xff, sizeof(buf)); + ASSERT_EQ(0x62a8ab43, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = i; + } + ASSERT_EQ(0x46dd794e, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = 31 - i; + } + ASSERT_EQ(0x113fdb5c, Value(buf, sizeof(buf))); + + unsigned char data[48] = { + 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x18, + 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + ASSERT_EQ(0xd9963a56, Value(reinterpret_cast<char*>(data), sizeof(data))); +} + +TEST(CRC, Values) { + ASSERT_NE(Value("a", 1), Value("foo", 3)); +} + +TEST(CRC, Extend) { + ASSERT_EQ(Value("hello world", 11), + Extend(Value("hello ", 6), "world", 5)); +} + +TEST(CRC, Mask) { + uint32_t crc = Value("foo", 3); + ASSERT_NE(crc, Mask(crc)); + ASSERT_NE(crc, Mask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc))))); +} + +} // namespace crc32c +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/env.cc b/src/leveldb/util/env.cc new file mode 100644 index 0000000000..c2600e964a --- /dev/null +++ b/src/leveldb/util/env.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +namespace leveldb { + +Env::~Env() { +} + +SequentialFile::~SequentialFile() { +} + +RandomAccessFile::~RandomAccessFile() { +} + +WritableFile::~WritableFile() { +} + +Logger::~Logger() { +} + +FileLock::~FileLock() { +} + +void Log(Logger* info_log, const char* format, ...) { + if (info_log != NULL) { + va_list ap; + va_start(ap, format); + info_log->Logv(format, ap); + va_end(ap); + } +} + +static Status DoWriteStringToFile(Env* env, const Slice& data, + const std::string& fname, + bool should_sync) { + WritableFile* file; + Status s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + s = file->Append(data); + if (s.ok() && should_sync) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; // Will auto-close if we did not close above + if (!s.ok()) { + env->DeleteFile(fname); + } + return s; +} + +Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, false); +} + +Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, true); +} + +Status ReadFileToString(Env* env, const std::string& fname, std::string* data) { + data->clear(); + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + return s; + } + static const int kBufferSize = 8192; + char* space = new char[kBufferSize]; + while (true) { + Slice fragment; + s = file->Read(kBufferSize, &fragment, space); + if (!s.ok()) { + break; + } + data->append(fragment.data(), fragment.size()); + if (fragment.empty()) { + break; + } + } + delete[] space; + delete file; + return s; +} + +EnvWrapper::~EnvWrapper() { +} + +} // namespace leveldb diff --git a/src/leveldb/util/env_boost.cc b/src/leveldb/util/env_boost.cc new file mode 100644 index 0000000000..055c657438 --- /dev/null +++ b/src/leveldb/util/env_boost.cc @@ -0,0 +1,591 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <deque> + +#ifdef LEVELDB_PLATFORM_WINDOWS +#include <windows.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <io.h> +#else +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/param.h> +#include <time.h> +#include <unistd.h> +#endif +#if defined(LEVELDB_PLATFORM_ANDROID) +#include <sys/stat.h> +#endif +#include "leveldb/env.h" +#include "leveldb/slice.h" + +#ifdef LEVELDB_PLATFORM_WINDOWS +#include "util/win_logger.h" +#else +#include "util/posix_logger.h" +#endif +#include "port/port.h" +#include "util/logging.h" + +#ifdef __linux +#include <sys/sysinfo.h> +#include <linux/unistd.h> +#endif + +#include <fstream> + +// Boost includes - see WINDOWS file to see which modules to install +#include <boost/date_time/gregorian/gregorian.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/filesystem/convenience.hpp> +#include <boost/thread/once.hpp> +#include <boost/thread/thread.hpp> +#include <boost/bind.hpp> +#include <boost/scoped_ptr.hpp> +#include <boost/interprocess/sync/file_lock.hpp> +#include <boost/thread/condition_variable.hpp> + +namespace leveldb { +namespace { + +// returns the ID of the current process +static boost::uint32_t current_process_id(void) { +#ifdef _WIN32 + return static_cast<boost::uint32_t>(::GetCurrentProcessId()); +#else + return static_cast<boost::uint32_t>(::getpid()); +#endif +} + +// returns the ID of the current thread +static boost::uint32_t current_thread_id(void) { +#ifdef _WIN32 + return static_cast<boost::uint32_t>(::GetCurrentThreadId()); +#else +#ifdef __linux + return static_cast<boost::uint32_t>(::syscall(__NR_gettid)); +#else + // just return the pid + return current_process_id(); +#endif +#endif +} + +static char global_read_only_buf[0x8000]; + +class PosixSequentialFile: public SequentialFile { + private: + std::string filename_; + FILE* file_; + + public: + PosixSequentialFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + virtual ~PosixSequentialFile() { fclose(file_); } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s; +#if defined(BSD) || defined(__MINGW32__) + // fread_unlocked doesn't exist on FreeBSD or MingW + size_t r = fread(scratch, 1, n, file_); +#else + size_t r = fread_unlocked(scratch, 1, n, file_); +#endif + *result = Slice(scratch, r); + if (r < n) { + if (feof(file_)) { + // We leave status as ok if we hit the end of the file + } else { + // A partial read with an error: return a non-ok status + s = Status::IOError(filename_, strerror(errno)); + } + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (fseek(file_, n, SEEK_CUR)) { + return Status::IOError(filename_, strerror(errno)); + } + return Status::OK(); + } +}; + +class PosixRandomAccessFile: public RandomAccessFile { + private: + std::string filename_; + int fd_; + mutable boost::mutex mu_; + + public: + PosixRandomAccessFile(const std::string& fname, int fd) + : filename_(fname), fd_(fd) { } + virtual ~PosixRandomAccessFile() { close(fd_); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; +#ifdef LEVELDB_PLATFORM_WINDOWS + // no pread on Windows so we emulate it with a mutex + boost::unique_lock<boost::mutex> lock(mu_); + + if (::_lseeki64(fd_, offset, SEEK_SET) == -1L) { + return Status::IOError(filename_, strerror(errno)); + } + + int r = ::_read(fd_, scratch, n); + *result = Slice(scratch, (r < 0) ? 0 : r); + lock.unlock(); +#else + ssize_t r = pread(fd_, scratch, n, static_cast<off_t>(offset)); + *result = Slice(scratch, (r < 0) ? 0 : r); +#endif + if (r < 0) { + // An error: return a non-ok status + s = Status::IOError(filename_, strerror(errno)); + } + return s; + } +}; + +// We preallocate up to an extra megabyte and use memcpy to append new +// data to the file. This is safe since we either properly close the +// file before reading from it, or for log files, the reading code +// knows enough to skip zero suffixes. + +class BoostFile : public WritableFile { + +public: + explicit BoostFile(std::string path) : path_(path), written_(0) { + Open(); + } + + virtual ~BoostFile() { + Close(); + } + +private: + void Open() { + // we truncate the file as implemented in env_posix + file_.open(path_.generic_string().c_str(), + std::ios_base::trunc | std::ios_base::out | std::ios_base::binary); + written_ = 0; + } + +public: + virtual Status Append(const Slice& data) { + Status result; + file_.write(data.data(), data.size()); + if (!file_.good()) { + result = Status::IOError( + path_.generic_string() + " Append", "cannot write"); + } + return result; + } + + virtual Status Close() { + Status result; + + try { + if (file_.is_open()) { + Sync(); + file_.close(); + } + } catch (const std::exception & e) { + result = Status::IOError(path_.generic_string() + " close", e.what()); + } + + return result; + } + + virtual Status Flush() { + file_.flush(); + return Status::OK(); + } + + virtual Status Sync() { + Status result; + try { + Flush(); + } catch (const std::exception & e) { + result = Status::IOError(path_.string() + " sync", e.what()); + } + + return result; + } + +private: + boost::filesystem::path path_; + boost::uint64_t written_; + std::ofstream file_; +}; + + + +class BoostFileLock : public FileLock { + public: + boost::interprocess::file_lock fl_; +}; + +class PosixEnv : public Env { + public: + PosixEnv(); + virtual ~PosixEnv() { + fprintf(stderr, "Destroying Env::Default()\n"); + exit(1); + } + + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + FILE* f = fopen(fname.c_str(), "rb"); + if (f == NULL) { + *result = NULL; + return Status::IOError(fname, strerror(errno)); + } else { + *result = new PosixSequentialFile(fname, f); + return Status::OK(); + } + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { +#ifdef LEVELDB_PLATFORM_WINDOWS + int fd = _open(fname.c_str(), _O_RDONLY | _O_RANDOM | _O_BINARY); +#else + int fd = open(fname.c_str(), O_RDONLY); +#endif + if (fd < 0) { + *result = NULL; + return Status::IOError(fname, strerror(errno)); + } + *result = new PosixRandomAccessFile(fname, fd); + return Status::OK(); + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + Status s; + try { + // will create a new empty file to write to + *result = new BoostFile(fname); + } + catch (const std::exception & e) { + s = Status::IOError(fname, e.what()); + } + + return s; + } + + virtual bool FileExists(const std::string& fname) { + return boost::filesystem::exists(fname); + } + + virtual Status GetChildren(const std::string& dir, + std::vector<std::string>* result) { + result->clear(); + + boost::system::error_code ec; + boost::filesystem::directory_iterator current(dir, ec); + if (ec != 0) { + return Status::IOError(dir, ec.message()); + } + + boost::filesystem::directory_iterator end; + + for(; current != end; ++current) { + result->push_back(current->path().filename().generic_string()); + } + + return Status::OK(); + } + + virtual Status DeleteFile(const std::string& fname) { + boost::system::error_code ec; + + boost::filesystem::remove(fname, ec); + + Status result; + + if (ec != 0) { + result = Status::IOError(fname, ec.message()); + } + + return result; + } + + virtual Status CreateDir(const std::string& name) { + Status result; + + if (boost::filesystem::exists(name) && + boost::filesystem::is_directory(name)) { + return result; + } + + boost::system::error_code ec; + + if (!boost::filesystem::create_directories(name, ec)) { + result = Status::IOError(name, ec.message()); + } + + return result; + }; + + virtual Status DeleteDir(const std::string& name) { + Status result; + + boost::system::error_code ec; + if (!boost::filesystem::remove_all(name, ec)) { + result = Status::IOError(name, ec.message()); + } + + return result; + }; + + virtual Status GetFileSize(const std::string& fname, uint64_t* size) { + boost::system::error_code ec; + + Status result; + + *size = static_cast<uint64_t>(boost::filesystem::file_size(fname, ec)); + if (ec != 0) { + *size = 0; + result = Status::IOError(fname, ec.message()); + } + + return result; + } + + virtual Status RenameFile(const std::string& src, const std::string& target) { + boost::system::error_code ec; + + boost::filesystem::rename(src, target, ec); + + Status result; + + if (ec != 0) { + result = Status::IOError(src, ec.message()); + } + + return result; + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = NULL; + + Status result; + + try { + if (!boost::filesystem::exists(fname)) { + std::ofstream of(fname.c_str(), std::ios_base::trunc | std::ios_base::out); + } + + assert(boost::filesystem::exists(fname)); + + boost::interprocess::file_lock fl(fname.c_str()); + BoostFileLock * my_lock = new BoostFileLock(); + fl.swap(my_lock->fl_); + if (!my_lock->fl_.try_lock()) { + return Status::IOError("database already in use: could not acquire exclusive lock"); + } + *lock = my_lock; + } catch (const std::exception & e) { + result = Status::IOError("lock " + fname, e.what()); + } + + return result; + } + + virtual Status UnlockFile(FileLock* lock) { + + Status result; + + try { + BoostFileLock * my_lock = static_cast<BoostFileLock *>(lock); + my_lock->fl_.unlock(); + delete my_lock; + } catch (const std::exception & e) { + result = Status::IOError("unlock", e.what()); + } + + return result; + } + + virtual void Schedule(void (*function)(void*), void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* result) { + boost::system::error_code ec; + boost::filesystem::path temp_dir = + boost::filesystem::temp_directory_path(ec); + if (ec != 0) { + temp_dir = "tmp"; + } + + temp_dir /= "leveldb_tests"; + temp_dir /= boost::lexical_cast<std::string>(current_process_id()); + + // Directory may already exist + CreateDir(temp_dir.generic_string()); + + *result = temp_dir.generic_string(); + + return Status::OK(); + } + +#ifndef LEVELDB_PLATFORM_WINDOWS + static uint64_t gettid() { + pthread_t tid = pthread_self(); + uint64_t thread_id = 0; + memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); + return thread_id; + } +#endif + + virtual Status NewLogger(const std::string& fname, Logger** result) { + FILE* f = fopen(fname.c_str(), "wt"); + if (f == NULL) { + *result = NULL; + return Status::IOError(fname, strerror(errno)); + } else { +#ifdef LEVELDB_PLATFORM_WINDOWS + *result = new WinLogger(f); +#else + *result = new PosixLogger(f, &PosixEnv::gettid); +#endif + return Status::OK(); + } + } + + virtual uint64_t NowMicros() { + return static_cast<uint64_t>( + boost::posix_time::microsec_clock::universal_time() + .time_of_day().total_microseconds()); + } + + virtual void SleepForMicroseconds(int micros) { + boost::this_thread::sleep(boost::posix_time::microseconds(micros)); + } + + private: + void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + exit(1); + } + } + + // BGThread() is the body of the background thread + void BGThread(); + + static void* BGThreadWrapper(void* arg) { + reinterpret_cast<PosixEnv*>(arg)->BGThread(); + return NULL; + } + + boost::mutex mu_; + boost::condition_variable bgsignal_; + boost::scoped_ptr<boost::thread> bgthread_; + + // Entry per Schedule() call + struct BGItem { void* arg; void (*function)(void*); }; + typedef std::deque<BGItem> BGQueue; + BGQueue queue_; +}; + +PosixEnv::PosixEnv() { } + +void PosixEnv::Schedule(void (*function)(void*), void* arg) { + boost::unique_lock<boost::mutex> lock(mu_); + + // Start background thread if necessary + if (!bgthread_) { + bgthread_.reset( + new boost::thread(boost::bind(&PosixEnv::BGThreadWrapper, this))); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; + + lock.unlock(); + + bgsignal_.notify_one(); + +} + +void PosixEnv::BGThread() { + while (true) { + // Wait until there is an item that is ready to run + boost::unique_lock<boost::mutex> lock(mu_); + + while (queue_.empty()) { + bgsignal_.wait(lock); + } + + void (*function)(void*) = queue_.front().function; + void* arg = queue_.front().arg; + queue_.pop_front(); + + lock.unlock(); + (*function)(arg); + } +} + +namespace { +struct StartThreadState { + void (*user_function)(void*); + void* arg; +}; +} + +static void* StartThreadWrapper(void* arg) { + StartThreadState* state = reinterpret_cast<StartThreadState*>(arg); + state->user_function(state->arg); + delete state; + return NULL; +} + +void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { + StartThreadState* state = new StartThreadState; + state->user_function = function; + state->arg = arg; + + boost::thread t(boost::bind(&StartThreadWrapper, state)); +} + +} + +static boost::once_flag once = BOOST_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { + ::memset(global_read_only_buf, 0, sizeof(global_read_only_buf)); + default_env = new PosixEnv; +} + +Env* Env::Default() { + boost::call_once(once, InitDefaultEnv); + + return default_env; +} + +} diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc new file mode 100644 index 0000000000..cb1f6fc05a --- /dev/null +++ b/src/leveldb/util/env_posix.cc @@ -0,0 +1,609 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <deque> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#if defined(LEVELDB_PLATFORM_ANDROID) +#include <sys/stat.h> +#endif +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/posix_logger.h" + +namespace leveldb { + +namespace { + +static Status IOError(const std::string& context, int err_number) { + return Status::IOError(context, strerror(err_number)); +} + +class PosixSequentialFile: public SequentialFile { + private: + std::string filename_; + FILE* file_; + + public: + PosixSequentialFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + virtual ~PosixSequentialFile() { fclose(file_); } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s; + size_t r = fread_unlocked(scratch, 1, n, file_); + *result = Slice(scratch, r); + if (r < n) { + if (feof(file_)) { + // We leave status as ok if we hit the end of the file + } else { + // A partial read with an error: return a non-ok status + s = IOError(filename_, errno); + } + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (fseek(file_, n, SEEK_CUR)) { + return IOError(filename_, errno); + } + return Status::OK(); + } +}; + +// pread() based random-access +class PosixRandomAccessFile: public RandomAccessFile { + private: + std::string filename_; + int fd_; + + public: + PosixRandomAccessFile(const std::string& fname, int fd) + : filename_(fname), fd_(fd) { } + virtual ~PosixRandomAccessFile() { close(fd_); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; + ssize_t r = pread(fd_, scratch, n, static_cast<off_t>(offset)); + *result = Slice(scratch, (r < 0) ? 0 : r); + if (r < 0) { + // An error: return a non-ok status + s = IOError(filename_, errno); + } + return s; + } +}; + +// mmap() based random-access +class PosixMmapReadableFile: public RandomAccessFile { + private: + std::string filename_; + void* mmapped_region_; + size_t length_; + + public: + // base[0,length-1] contains the mmapped contents of the file. + PosixMmapReadableFile(const std::string& fname, void* base, size_t length) + : filename_(fname), mmapped_region_(base), length_(length) { } + virtual ~PosixMmapReadableFile() { munmap(mmapped_region_, length_); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; + if (offset + n > length_) { + *result = Slice(); + s = IOError(filename_, EINVAL); + } else { + *result = Slice(reinterpret_cast<char*>(mmapped_region_) + offset, n); + } + return s; + } +}; + +// We preallocate up to an extra megabyte and use memcpy to append new +// data to the file. This is safe since we either properly close the +// file before reading from it, or for log files, the reading code +// knows enough to skip zero suffixes. +class PosixMmapFile : public WritableFile { + private: + std::string filename_; + int fd_; + size_t page_size_; + size_t map_size_; // How much extra memory to map at a time + char* base_; // The mapped region + char* limit_; // Limit of the mapped region + char* dst_; // Where to write next (in range [base_,limit_]) + char* last_sync_; // Where have we synced up to + uint64_t file_offset_; // Offset of base_ in file + + // Have we done an munmap of unsynced data? + bool pending_sync_; + + // Roundup x to a multiple of y + static size_t Roundup(size_t x, size_t y) { + return ((x + y - 1) / y) * y; + } + + size_t TruncateToPageBoundary(size_t s) { + s -= (s & (page_size_ - 1)); + assert((s % page_size_) == 0); + return s; + } + + bool UnmapCurrentRegion() { + bool result = true; + if (base_ != NULL) { + if (last_sync_ < limit_) { + // Defer syncing this data until next Sync() call, if any + pending_sync_ = true; + } + if (munmap(base_, limit_ - base_) != 0) { + result = false; + } + file_offset_ += limit_ - base_; + base_ = NULL; + limit_ = NULL; + last_sync_ = NULL; + dst_ = NULL; + + // Increase the amount we map the next time, but capped at 1MB + if (map_size_ < (1<<20)) { + map_size_ *= 2; + } + } + return result; + } + + bool MapNewRegion() { + assert(base_ == NULL); + if (ftruncate(fd_, file_offset_ + map_size_) < 0) { + return false; + } + void* ptr = mmap(NULL, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_, file_offset_); + if (ptr == MAP_FAILED) { + return false; + } + base_ = reinterpret_cast<char*>(ptr); + limit_ = base_ + map_size_; + dst_ = base_; + last_sync_ = base_; + return true; + } + + public: + PosixMmapFile(const std::string& fname, int fd, size_t page_size) + : filename_(fname), + fd_(fd), + page_size_(page_size), + map_size_(Roundup(65536, page_size)), + base_(NULL), + limit_(NULL), + dst_(NULL), + last_sync_(NULL), + file_offset_(0), + pending_sync_(false) { + assert((page_size & (page_size - 1)) == 0); + } + + + ~PosixMmapFile() { + if (fd_ >= 0) { + PosixMmapFile::Close(); + } + } + + virtual Status Append(const Slice& data) { + const char* src = data.data(); + size_t left = data.size(); + while (left > 0) { + assert(base_ <= dst_); + assert(dst_ <= limit_); + size_t avail = limit_ - dst_; + if (avail == 0) { + if (!UnmapCurrentRegion() || + !MapNewRegion()) { + return IOError(filename_, errno); + } + } + + size_t n = (left <= avail) ? left : avail; + memcpy(dst_, src, n); + dst_ += n; + src += n; + left -= n; + } + return Status::OK(); + } + + virtual Status Close() { + Status s; + size_t unused = limit_ - dst_; + if (!UnmapCurrentRegion()) { + s = IOError(filename_, errno); + } else if (unused > 0) { + // Trim the extra space at the end of the file + if (ftruncate(fd_, file_offset_ - unused) < 0) { + s = IOError(filename_, errno); + } + } + + if (close(fd_) < 0) { + if (s.ok()) { + s = IOError(filename_, errno); + } + } + + fd_ = -1; + base_ = NULL; + limit_ = NULL; + return s; + } + + virtual Status Flush() { + return Status::OK(); + } + + virtual Status Sync() { + Status s; + + if (pending_sync_) { + // Some unmapped data was not synced + pending_sync_ = false; + if (fdatasync(fd_) < 0) { + s = IOError(filename_, errno); + } + } + + if (dst_ > last_sync_) { + // Find the beginnings of the pages that contain the first and last + // bytes to be synced. + size_t p1 = TruncateToPageBoundary(last_sync_ - base_); + size_t p2 = TruncateToPageBoundary(dst_ - base_ - 1); + last_sync_ = dst_; + if (msync(base_ + p1, p2 - p1 + page_size_, MS_SYNC) < 0) { + s = IOError(filename_, errno); + } + } + + return s; + } +}; + +static int LockOrUnlock(int fd, bool lock) { + errno = 0; + struct flock f; + memset(&f, 0, sizeof(f)); + f.l_type = (lock ? F_WRLCK : F_UNLCK); + f.l_whence = SEEK_SET; + f.l_start = 0; + f.l_len = 0; // Lock/unlock entire file + return fcntl(fd, F_SETLK, &f); +} + +class PosixFileLock : public FileLock { + public: + int fd_; +}; + +class PosixEnv : public Env { + public: + PosixEnv(); + virtual ~PosixEnv() { + fprintf(stderr, "Destroying Env::Default()\n"); + exit(1); + } + + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + FILE* f = fopen(fname.c_str(), "r"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixSequentialFile(fname, f); + return Status::OK(); + } + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + *result = NULL; + Status s; + int fd = open(fname.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(fname, errno); + } else if (sizeof(void*) >= 8) { + // Use mmap when virtual address-space is plentiful. + uint64_t size; + s = GetFileSize(fname, &size); + if (s.ok()) { + void* base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (base != MAP_FAILED) { + *result = new PosixMmapReadableFile(fname, base, size); + } else { + s = IOError(fname, errno); + } + } + close(fd); + } else { + *result = new PosixRandomAccessFile(fname, fd); + } + return s; + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + Status s; + const int fd = open(fname.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd < 0) { + *result = NULL; + s = IOError(fname, errno); + } else { + *result = new PosixMmapFile(fname, fd, page_size_); + } + return s; + } + + virtual bool FileExists(const std::string& fname) { + return access(fname.c_str(), F_OK) == 0; + } + + virtual Status GetChildren(const std::string& dir, + std::vector<std::string>* result) { + result->clear(); + DIR* d = opendir(dir.c_str()); + if (d == NULL) { + return IOError(dir, errno); + } + struct dirent* entry; + while ((entry = readdir(d)) != NULL) { + result->push_back(entry->d_name); + } + closedir(d); + return Status::OK(); + } + + virtual Status DeleteFile(const std::string& fname) { + Status result; + if (unlink(fname.c_str()) != 0) { + result = IOError(fname, errno); + } + return result; + }; + + virtual Status CreateDir(const std::string& name) { + Status result; + if (mkdir(name.c_str(), 0755) != 0) { + result = IOError(name, errno); + } + return result; + }; + + virtual Status DeleteDir(const std::string& name) { + Status result; + if (rmdir(name.c_str()) != 0) { + result = IOError(name, errno); + } + return result; + }; + + virtual Status GetFileSize(const std::string& fname, uint64_t* size) { + Status s; + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + *size = 0; + s = IOError(fname, errno); + } else { + *size = sbuf.st_size; + } + return s; + } + + virtual Status RenameFile(const std::string& src, const std::string& target) { + Status result; + if (rename(src.c_str(), target.c_str()) != 0) { + result = IOError(src, errno); + } + return result; + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = NULL; + Status result; + int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + if (fd < 0) { + result = IOError(fname, errno); + } else if (LockOrUnlock(fd, true) == -1) { + result = IOError("lock " + fname, errno); + close(fd); + } else { + PosixFileLock* my_lock = new PosixFileLock; + my_lock->fd_ = fd; + *lock = my_lock; + } + return result; + } + + virtual Status UnlockFile(FileLock* lock) { + PosixFileLock* my_lock = reinterpret_cast<PosixFileLock*>(lock); + Status result; + if (LockOrUnlock(my_lock->fd_, false) == -1) { + result = IOError("unlock", errno); + } + close(my_lock->fd_); + delete my_lock; + return result; + } + + virtual void Schedule(void (*function)(void*), void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* result) { + const char* env = getenv("TEST_TMPDIR"); + if (env && env[0] != '\0') { + *result = env; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid())); + *result = buf; + } + // Directory may already exist + CreateDir(*result); + return Status::OK(); + } + + static uint64_t gettid() { + pthread_t tid = pthread_self(); + uint64_t thread_id = 0; + memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); + return thread_id; + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + FILE* f = fopen(fname.c_str(), "w"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixLogger(f, &PosixEnv::gettid); + return Status::OK(); + } + } + + virtual uint64_t NowMicros() { + struct timeval tv; + gettimeofday(&tv, NULL); + return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec; + } + + virtual void SleepForMicroseconds(int micros) { + usleep(micros); + } + + private: + void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + exit(1); + } + } + + // BGThread() is the body of the background thread + void BGThread(); + static void* BGThreadWrapper(void* arg) { + reinterpret_cast<PosixEnv*>(arg)->BGThread(); + return NULL; + } + + size_t page_size_; + pthread_mutex_t mu_; + pthread_cond_t bgsignal_; + pthread_t bgthread_; + bool started_bgthread_; + + // Entry per Schedule() call + struct BGItem { void* arg; void (*function)(void*); }; + typedef std::deque<BGItem> BGQueue; + BGQueue queue_; +}; + +PosixEnv::PosixEnv() : page_size_(getpagesize()), + started_bgthread_(false) { + PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); + PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); +} + +void PosixEnv::Schedule(void (*function)(void*), void* arg) { + PthreadCall("lock", pthread_mutex_lock(&mu_)); + + // Start background thread if necessary + if (!started_bgthread_) { + started_bgthread_ = true; + PthreadCall( + "create thread", + pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); + } + + // If the queue is currently empty, the background thread may currently be + // waiting. + if (queue_.empty()) { + PthreadCall("signal", pthread_cond_signal(&bgsignal_)); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); +} + +void PosixEnv::BGThread() { + while (true) { + // Wait until there is an item that is ready to run + PthreadCall("lock", pthread_mutex_lock(&mu_)); + while (queue_.empty()) { + PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); + } + + void (*function)(void*) = queue_.front().function; + void* arg = queue_.front().arg; + queue_.pop_front(); + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); + (*function)(arg); + } +} + +namespace { +struct StartThreadState { + void (*user_function)(void*); + void* arg; +}; +} +static void* StartThreadWrapper(void* arg) { + StartThreadState* state = reinterpret_cast<StartThreadState*>(arg); + state->user_function(state->arg); + delete state; + return NULL; +} + +void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { + pthread_t t; + StartThreadState* state = new StartThreadState; + state->user_function = function; + state->arg = arg; + PthreadCall("start thread", + pthread_create(&t, NULL, &StartThreadWrapper, state)); +} + +} // namespace + +static pthread_once_t once = PTHREAD_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { default_env = new PosixEnv; } + +Env* Env::Default() { + pthread_once(&once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb diff --git a/src/leveldb/util/env_test.cc b/src/leveldb/util/env_test.cc new file mode 100644 index 0000000000..b72cb44384 --- /dev/null +++ b/src/leveldb/util/env_test.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/testharness.h" + +namespace leveldb { + +static const int kDelayMicros = 100000; + +class EnvPosixTest { + private: + port::Mutex mu_; + std::string events_; + + public: + Env* env_; + EnvPosixTest() : env_(Env::Default()) { } +}; + +static void SetBool(void* ptr) { + reinterpret_cast<port::AtomicPointer*>(ptr)->NoBarrier_Store(ptr); +} + +TEST(EnvPosixTest, RunImmediately) { + port::AtomicPointer called (NULL); + env_->Schedule(&SetBool, &called); + Env::Default()->SleepForMicroseconds(kDelayMicros); + ASSERT_TRUE(called.NoBarrier_Load() != NULL); +} + +TEST(EnvPosixTest, RunMany) { + port::AtomicPointer last_id (NULL); + + struct CB { + port::AtomicPointer* last_id_ptr; // Pointer to shared slot + uintptr_t id; // Order# for the execution of this callback + + CB(port::AtomicPointer* p, int i) : last_id_ptr(p), id(i) { } + + static void Run(void* v) { + CB* cb = reinterpret_cast<CB*>(v); + void* cur = cb->last_id_ptr->NoBarrier_Load(); + ASSERT_EQ(cb->id-1, reinterpret_cast<uintptr_t>(cur)); + cb->last_id_ptr->Release_Store(reinterpret_cast<void*>(cb->id)); + } + }; + + // Schedule in different order than start time + CB cb1(&last_id, 1); + CB cb2(&last_id, 2); + CB cb3(&last_id, 3); + CB cb4(&last_id, 4); + env_->Schedule(&CB::Run, &cb1); + env_->Schedule(&CB::Run, &cb2); + env_->Schedule(&CB::Run, &cb3); + env_->Schedule(&CB::Run, &cb4); + + Env::Default()->SleepForMicroseconds(kDelayMicros); + void* cur = last_id.Acquire_Load(); + ASSERT_EQ(4, reinterpret_cast<uintptr_t>(cur)); +} + +struct State { + port::Mutex mu; + int val; + int num_running; +}; + +static void ThreadBody(void* arg) { + State* s = reinterpret_cast<State*>(arg); + s->mu.Lock(); + s->val += 1; + s->num_running -= 1; + s->mu.Unlock(); +} + +TEST(EnvPosixTest, StartThread) { + State state; + state.val = 0; + state.num_running = 3; + for (int i = 0; i < 3; i++) { + env_->StartThread(&ThreadBody, &state); + } + while (true) { + state.mu.Lock(); + int num = state.num_running; + state.mu.Unlock(); + if (num == 0) { + break; + } + Env::Default()->SleepForMicroseconds(kDelayMicros); + } + ASSERT_EQ(state.val, 3); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/filter_policy.cc b/src/leveldb/util/filter_policy.cc new file mode 100644 index 0000000000..7b045c8c91 --- /dev/null +++ b/src/leveldb/util/filter_policy.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +namespace leveldb { + +FilterPolicy::~FilterPolicy() { } + +} // namespace leveldb diff --git a/src/leveldb/util/hash.cc b/src/leveldb/util/hash.cc new file mode 100644 index 0000000000..ba1818082d --- /dev/null +++ b/src/leveldb/util/hash.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <string.h> +#include "util/coding.h" +#include "util/hash.h" + +namespace leveldb { + +uint32_t Hash(const char* data, size_t n, uint32_t seed) { + // Similar to murmur hash + const uint32_t m = 0xc6a4a793; + const uint32_t r = 24; + const char* limit = data + n; + uint32_t h = seed ^ (n * m); + + // Pick up four bytes at a time + while (data + 4 <= limit) { + uint32_t w = DecodeFixed32(data); + data += 4; + h += w; + h *= m; + h ^= (h >> 16); + } + + // Pick up remaining bytes + switch (limit - data) { + case 3: + h += data[2] << 16; + // fall through + case 2: + h += data[1] << 8; + // fall through + case 1: + h += data[0]; + h *= m; + h ^= (h >> r); + break; + } + return h; +} + + +} // namespace leveldb diff --git a/src/leveldb/util/hash.h b/src/leveldb/util/hash.h new file mode 100644 index 0000000000..8889d56be8 --- /dev/null +++ b/src/leveldb/util/hash.h @@ -0,0 +1,19 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Simple hash function used for internal data structures + +#ifndef STORAGE_LEVELDB_UTIL_HASH_H_ +#define STORAGE_LEVELDB_UTIL_HASH_H_ + +#include <stddef.h> +#include <stdint.h> + +namespace leveldb { + +extern uint32_t Hash(const char* data, size_t n, uint32_t seed); + +} + +#endif // STORAGE_LEVELDB_UTIL_HASH_H_ diff --git a/src/leveldb/util/histogram.cc b/src/leveldb/util/histogram.cc new file mode 100644 index 0000000000..bb95f583ea --- /dev/null +++ b/src/leveldb/util/histogram.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <math.h> +#include <stdio.h> +#include "port/port.h" +#include "util/histogram.h" + +namespace leveldb { + +const double Histogram::kBucketLimit[kNumBuckets] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, + 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450, + 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000, + 3500, 4000, 4500, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, + 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 60000, + 70000, 80000, 90000, 100000, 120000, 140000, 160000, 180000, 200000, + 250000, 300000, 350000, 400000, 450000, 500000, 600000, 700000, 800000, + 900000, 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2500000, + 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, 7000000, 8000000, + 9000000, 10000000, 12000000, 14000000, 16000000, 18000000, 20000000, + 25000000, 30000000, 35000000, 40000000, 45000000, 50000000, 60000000, + 70000000, 80000000, 90000000, 100000000, 120000000, 140000000, 160000000, + 180000000, 200000000, 250000000, 300000000, 350000000, 400000000, + 450000000, 500000000, 600000000, 700000000, 800000000, 900000000, + 1000000000, 1200000000, 1400000000, 1600000000, 1800000000, 2000000000, + 2500000000.0, 3000000000.0, 3500000000.0, 4000000000.0, 4500000000.0, + 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0, + 1e200, +}; + +void Histogram::Clear() { + min_ = kBucketLimit[kNumBuckets-1]; + max_ = 0; + num_ = 0; + sum_ = 0; + sum_squares_ = 0; + for (int i = 0; i < kNumBuckets; i++) { + buckets_[i] = 0; + } +} + +void Histogram::Add(double value) { + // Linear search is fast enough for our usage in db_bench + int b = 0; + while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) { + b++; + } + buckets_[b] += 1.0; + if (min_ > value) min_ = value; + if (max_ < value) max_ = value; + num_++; + sum_ += value; + sum_squares_ += (value * value); +} + +void Histogram::Merge(const Histogram& other) { + if (other.min_ < min_) min_ = other.min_; + if (other.max_ > max_) max_ = other.max_; + num_ += other.num_; + sum_ += other.sum_; + sum_squares_ += other.sum_squares_; + for (int b = 0; b < kNumBuckets; b++) { + buckets_[b] += other.buckets_[b]; + } +} + +double Histogram::Median() const { + return Percentile(50.0); +} + +double Histogram::Percentile(double p) const { + double threshold = num_ * (p / 100.0); + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + sum += buckets_[b]; + if (sum >= threshold) { + // Scale linearly within this bucket + double left_point = (b == 0) ? 0 : kBucketLimit[b-1]; + double right_point = kBucketLimit[b]; + double left_sum = sum - buckets_[b]; + double right_sum = sum; + double pos = (threshold - left_sum) / (right_sum - left_sum); + double r = left_point + (right_point - left_point) * pos; + if (r < min_) r = min_; + if (r > max_) r = max_; + return r; + } + } + return max_; +} + +double Histogram::Average() const { + if (num_ == 0.0) return 0; + return sum_ / num_; +} + +double Histogram::StandardDeviation() const { + if (num_ == 0.0) return 0; + double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_); + return sqrt(variance); +} + +std::string Histogram::ToString() const { + std::string r; + char buf[200]; + snprintf(buf, sizeof(buf), + "Count: %.0f Average: %.4f StdDev: %.2f\n", + num_, Average(), StandardDeviation()); + r.append(buf); + snprintf(buf, sizeof(buf), + "Min: %.4f Median: %.4f Max: %.4f\n", + (num_ == 0.0 ? 0.0 : min_), Median(), max_); + r.append(buf); + r.append("------------------------------------------------------\n"); + const double mult = 100.0 / num_; + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + if (buckets_[b] <= 0.0) continue; + sum += buckets_[b]; + snprintf(buf, sizeof(buf), + "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", + ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left + kBucketLimit[b], // right + buckets_[b], // count + mult * buckets_[b], // percentage + mult * sum); // cumulative percentage + r.append(buf); + + // Add hash marks based on percentage; 20 marks for 100%. + int marks = static_cast<int>(20*(buckets_[b] / num_) + 0.5); + r.append(marks, '#'); + r.push_back('\n'); + } + return r; +} + +} // namespace leveldb diff --git a/src/leveldb/util/histogram.h b/src/leveldb/util/histogram.h new file mode 100644 index 0000000000..1ef9f3c8ab --- /dev/null +++ b/src/leveldb/util/histogram.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ +#define STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ + +#include <string> + +namespace leveldb { + +class Histogram { + public: + Histogram() { } + ~Histogram() { } + + void Clear(); + void Add(double value); + void Merge(const Histogram& other); + + std::string ToString() const; + + private: + double min_; + double max_; + double num_; + double sum_; + double sum_squares_; + + enum { kNumBuckets = 154 }; + static const double kBucketLimit[kNumBuckets]; + double buckets_[kNumBuckets]; + + double Median() const; + double Percentile(double p) const; + double Average() const; + double StandardDeviation() const; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ diff --git a/src/leveldb/util/logging.cc b/src/leveldb/util/logging.cc new file mode 100644 index 0000000000..22cf278512 --- /dev/null +++ b/src/leveldb/util/logging.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/logging.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include "leveldb/env.h" +#include "leveldb/slice.h" + +namespace leveldb { + +void AppendNumberTo(std::string* str, uint64_t num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%llu", (unsigned long long) num); + str->append(buf); +} + +void AppendEscapedStringTo(std::string* str, const Slice& value) { + for (size_t i = 0; i < value.size(); i++) { + char c = value[i]; + if (c >= ' ' && c <= '~') { + str->push_back(c); + } else { + char buf[10]; + snprintf(buf, sizeof(buf), "\\x%02x", + static_cast<unsigned int>(c) & 0xff); + str->append(buf); + } + } +} + +std::string NumberToString(uint64_t num) { + std::string r; + AppendNumberTo(&r, num); + return r; +} + +std::string EscapeString(const Slice& value) { + std::string r; + AppendEscapedStringTo(&r, value); + return r; +} + +bool ConsumeChar(Slice* in, char c) { + if (!in->empty() && (*in)[0] == c) { + in->remove_prefix(1); + return true; + } else { + return false; + } +} + +bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { + uint64_t v = 0; + int digits = 0; + while (!in->empty()) { + char c = (*in)[0]; + if (c >= '0' && c <= '9') { + ++digits; + const int delta = (c - '0'); + static const uint64_t kMaxUint64 = ~static_cast<uint64_t>(0); + if (v > kMaxUint64/10 || + (v == kMaxUint64/10 && delta > kMaxUint64%10)) { + // Overflow + return false; + } + v = (v * 10) + delta; + in->remove_prefix(1); + } else { + break; + } + } + *val = v; + return (digits > 0); +} + +} // namespace leveldb diff --git a/src/leveldb/util/logging.h b/src/leveldb/util/logging.h new file mode 100644 index 0000000000..b0c5da813e --- /dev/null +++ b/src/leveldb/util/logging.h @@ -0,0 +1,47 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Must not be included from any .h files to avoid polluting the namespace +// with macros. + +#ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_ +#define STORAGE_LEVELDB_UTIL_LOGGING_H_ + +#include <stdio.h> +#include <stdint.h> +#include <string> +#include "port/port.h" + +namespace leveldb { + +class Slice; +class WritableFile; + +// Append a human-readable printout of "num" to *str +extern void AppendNumberTo(std::string* str, uint64_t num); + +// Append a human-readable printout of "value" to *str. +// Escapes any non-printable characters found in "value". +extern void AppendEscapedStringTo(std::string* str, const Slice& value); + +// Return a human-readable printout of "num" +extern std::string NumberToString(uint64_t num); + +// Return a human-readable version of "value". +// Escapes any non-printable characters found in "value". +extern std::string EscapeString(const Slice& value); + +// If *in starts with "c", advances *in past the first character and +// returns true. Otherwise, returns false. +extern bool ConsumeChar(Slice* in, char c); + +// Parse a human-readable number from "*in" into *value. On success, +// advances "*in" past the consumed number and sets "*val" to the +// numeric value. Otherwise, returns false and leaves *in in an +// unspecified state. +extern bool ConsumeDecimalNumber(Slice* in, uint64_t* val); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_LOGGING_H_ diff --git a/src/leveldb/util/mutexlock.h b/src/leveldb/util/mutexlock.h new file mode 100644 index 0000000000..c3f3306d3e --- /dev/null +++ b/src/leveldb/util/mutexlock.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ +#define STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ + +#include "port/port.h" + +namespace leveldb { + +// Helper class that locks a mutex on construction and unlocks the mutex when +// the destructor of the MutexLock object is invoked. +// +// Typical usage: +// +// void MyClass::MyMethod() { +// MutexLock l(&mu_); // mu_ is an instance variable +// ... some complex code, possibly with multiple return paths ... +// } + +class MutexLock { + public: + explicit MutexLock(port::Mutex *mu) : mu_(mu) { + this->mu_->Lock(); + } + ~MutexLock() { this->mu_->Unlock(); } + + private: + port::Mutex *const mu_; + // No copying allowed + MutexLock(const MutexLock&); + void operator=(const MutexLock&); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ diff --git a/src/leveldb/util/options.cc b/src/leveldb/util/options.cc new file mode 100644 index 0000000000..76af5b9302 --- /dev/null +++ b/src/leveldb/util/options.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/options.h" + +#include "leveldb/comparator.h" +#include "leveldb/env.h" + +namespace leveldb { + +Options::Options() + : comparator(BytewiseComparator()), + create_if_missing(false), + error_if_exists(false), + paranoid_checks(false), + env(Env::Default()), + info_log(NULL), + write_buffer_size(4<<20), + max_open_files(1000), + block_cache(NULL), + block_size(4096), + block_restart_interval(16), + compression(kSnappyCompression), + filter_policy(NULL) { +} + + +} // namespace leveldb diff --git a/src/leveldb/util/posix_logger.h b/src/leveldb/util/posix_logger.h new file mode 100644 index 0000000000..9741b1afad --- /dev/null +++ b/src/leveldb/util/posix_logger.h @@ -0,0 +1,98 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Logger implementation that can be shared by all environments +// where enough posix functionality is available. + +#ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ + +#include <algorithm> +#include <stdio.h> +#include <sys/time.h> +#include <time.h> +#include "leveldb/env.h" + +namespace leveldb { + +class PosixLogger : public Logger { + private: + FILE* file_; + uint64_t (*gettid_)(); // Return the thread id for the current thread + public: + PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { } + virtual ~PosixLogger() { + fclose(file_); + } + virtual void Logv(const char* format, va_list ap) { + const uint64_t thread_id = (*gettid_)(); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + struct timeval now_tv; + gettimeofday(&now_tv, NULL); + const time_t seconds = now_tv.tv_sec; + struct tm t; + localtime_r(&seconds, &t); + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + t.tm_year + 1900, + t.tm_mon + 1, + t.tm_mday, + t.tm_hour, + t.tm_min, + t.tm_sec, + static_cast<int>(now_tv.tv_usec), + static_cast<long long unsigned int>(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + fwrite(base, 1, p - base, file_); + fflush(file_); + if (base != buffer) { + delete[] base; + } + break; + } + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ diff --git a/src/leveldb/util/random.h b/src/leveldb/util/random.h new file mode 100644 index 0000000000..07538242ea --- /dev/null +++ b/src/leveldb/util/random.h @@ -0,0 +1,59 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_RANDOM_H_ +#define STORAGE_LEVELDB_UTIL_RANDOM_H_ + +#include <stdint.h> + +namespace leveldb { + +// A very simple random number generator. Not especially good at +// generating truly random bits, but good enough for our needs in this +// package. +class Random { + private: + uint32_t seed_; + public: + explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { } + uint32_t Next() { + static const uint32_t M = 2147483647L; // 2^31-1 + static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 + // We are computing + // seed_ = (seed_ * A) % M, where M = 2^31-1 + // + // seed_ must not be zero or M, or else all subsequent computed values + // will be zero or M respectively. For all other values, seed_ will end + // up cycling through every number in [1,M-1] + uint64_t product = seed_ * A; + + // Compute (product % M) using the fact that ((x << 31) % M) == x. + seed_ = static_cast<uint32_t>((product >> 31) + (product & M)); + // The first reduction may overflow by 1 bit, so we may need to + // repeat. mod == M is not possible; using > allows the faster + // sign-bit-based test. + if (seed_ > M) { + seed_ -= M; + } + return seed_; + } + // Returns a uniformly distributed value in the range [0..n-1] + // REQUIRES: n > 0 + uint32_t Uniform(int n) { return Next() % n; } + + // Randomly returns true ~"1/n" of the time, and false otherwise. + // REQUIRES: n > 0 + bool OneIn(int n) { return (Next() % n) == 0; } + + // Skewed: pick "base" uniformly from range [0,max_log] and then + // return "base" random bits. The effect is to pick a number in the + // range [0,2^max_log-1] with exponential bias towards smaller numbers. + uint32_t Skewed(int max_log) { + return Uniform(1 << Uniform(max_log + 1)); + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_RANDOM_H_ diff --git a/src/leveldb/util/status.cc b/src/leveldb/util/status.cc new file mode 100644 index 0000000000..a44f35b314 --- /dev/null +++ b/src/leveldb/util/status.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include <stdio.h> +#include "port/port.h" +#include "leveldb/status.h" + +namespace leveldb { + +const char* Status::CopyState(const char* state) { + uint32_t size; + memcpy(&size, state, sizeof(size)); + char* result = new char[size + 5]; + memcpy(result, state, size + 5); + return result; +} + +Status::Status(Code code, const Slice& msg, const Slice& msg2) { + assert(code != kOk); + const uint32_t len1 = msg.size(); + const uint32_t len2 = msg2.size(); + const uint32_t size = len1 + (len2 ? (2 + len2) : 0); + char* result = new char[size + 5]; + memcpy(result, &size, sizeof(size)); + result[4] = static_cast<char>(code); + memcpy(result + 5, msg.data(), len1); + if (len2) { + result[5 + len1] = ':'; + result[6 + len1] = ' '; + memcpy(result + 7 + len1, msg2.data(), len2); + } + state_ = result; +} + +std::string Status::ToString() const { + if (state_ == NULL) { + return "OK"; + } else { + char tmp[30]; + const char* type; + switch (code()) { + case kOk: + type = "OK"; + break; + case kNotFound: + type = "NotFound: "; + break; + case kCorruption: + type = "Corruption: "; + break; + case kNotSupported: + type = "Not implemented: "; + break; + case kInvalidArgument: + type = "Invalid argument: "; + break; + case kIOError: + type = "IO error: "; + break; + default: + snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", + static_cast<int>(code())); + type = tmp; + break; + } + std::string result(type); + uint32_t length; + memcpy(&length, state_, sizeof(length)); + result.append(state_ + 5, length); + return result; + } +} + +} // namespace leveldb diff --git a/src/leveldb/util/testharness.cc b/src/leveldb/util/testharness.cc new file mode 100644 index 0000000000..eb1bdd554a --- /dev/null +++ b/src/leveldb/util/testharness.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testharness.h" + +#include <string> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> + +namespace leveldb { +namespace test { + +namespace { +struct Test { + const char* base; + const char* name; + void (*func)(); +}; +std::vector<Test>* tests; +} + +bool RegisterTest(const char* base, const char* name, void (*func)()) { + if (tests == NULL) { + tests = new std::vector<Test>; + } + Test t; + t.base = base; + t.name = name; + t.func = func; + tests->push_back(t); + return true; +} + +int RunAllTests() { + const char* matcher = getenv("LEVELDB_TESTS"); + + int num = 0; + if (tests != NULL) { + for (int i = 0; i < tests->size(); i++) { + const Test& t = (*tests)[i]; + if (matcher != NULL) { + std::string name = t.base; + name.push_back('.'); + name.append(t.name); + if (strstr(name.c_str(), matcher) == NULL) { + continue; + } + } + fprintf(stderr, "==== Test %s.%s\n", t.base, t.name); + (*t.func)(); + ++num; + } + } + fprintf(stderr, "==== PASSED %d tests\n", num); + return 0; +} + +std::string TmpDir() { + std::string dir; + Status s = Env::Default()->GetTestDirectory(&dir); + ASSERT_TRUE(s.ok()) << s.ToString(); + return dir; +} + +int RandomSeed() { + const char* env = getenv("TEST_RANDOM_SEED"); + int result = (env != NULL ? atoi(env) : 301); + if (result <= 0) { + result = 301; + } + return result; +} + +} // namespace test +} // namespace leveldb diff --git a/src/leveldb/util/testharness.h b/src/leveldb/util/testharness.h new file mode 100644 index 0000000000..da4fe68bb4 --- /dev/null +++ b/src/leveldb/util/testharness.h @@ -0,0 +1,138 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ +#define STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ + +#include <stdio.h> +#include <stdlib.h> +#include <sstream> +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Run some of the tests registered by the TEST() macro. If the +// environment variable "LEVELDB_TESTS" is not set, runs all tests. +// Otherwise, runs only the tests whose name contains the value of +// "LEVELDB_TESTS" as a substring. E.g., suppose the tests are: +// TEST(Foo, Hello) { ... } +// TEST(Foo, World) { ... } +// LEVELDB_TESTS=Hello will run the first test +// LEVELDB_TESTS=o will run both tests +// LEVELDB_TESTS=Junk will run no tests +// +// Returns 0 if all tests pass. +// Dies or returns a non-zero value if some test fails. +extern int RunAllTests(); + +// Return the directory to use for temporary storage. +extern std::string TmpDir(); + +// Return a randomization seed for this run. Typically returns the +// same number on repeated invocations of this binary, but automated +// runs may be able to vary the seed. +extern int RandomSeed(); + +// An instance of Tester is allocated to hold temporary state during +// the execution of an assertion. +class Tester { + private: + bool ok_; + const char* fname_; + int line_; + std::stringstream ss_; + + public: + Tester(const char* f, int l) + : ok_(true), fname_(f), line_(l) { + } + + ~Tester() { + if (!ok_) { + fprintf(stderr, "%s:%d:%s\n", fname_, line_, ss_.str().c_str()); + exit(1); + } + } + + Tester& Is(bool b, const char* msg) { + if (!b) { + ss_ << " Assertion failure " << msg; + ok_ = false; + } + return *this; + } + + Tester& IsOk(const Status& s) { + if (!s.ok()) { + ss_ << " " << s.ToString(); + ok_ = false; + } + return *this; + } + +#define BINARY_OP(name,op) \ + template <class X, class Y> \ + Tester& name(const X& x, const Y& y) { \ + if (! (x op y)) { \ + ss_ << " failed: " << x << (" " #op " ") << y; \ + ok_ = false; \ + } \ + return *this; \ + } + + BINARY_OP(IsEq, ==) + BINARY_OP(IsNe, !=) + BINARY_OP(IsGe, >=) + BINARY_OP(IsGt, >) + BINARY_OP(IsLe, <=) + BINARY_OP(IsLt, <) +#undef BINARY_OP + + // Attach the specified value to the error message if an error has occurred + template <class V> + Tester& operator<<(const V& value) { + if (!ok_) { + ss_ << " " << value; + } + return *this; + } +}; + +#define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c) +#define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s)) +#define ASSERT_EQ(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b)) +#define ASSERT_NE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a),(b)) +#define ASSERT_GE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a),(b)) +#define ASSERT_GT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a),(b)) +#define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b)) +#define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b)) + +#define TCONCAT(a,b) TCONCAT1(a,b) +#define TCONCAT1(a,b) a##b + +#define TEST(base,name) \ +class TCONCAT(_Test_,name) : public base { \ + public: \ + void _Run(); \ + static void _RunIt() { \ + TCONCAT(_Test_,name) t; \ + t._Run(); \ + } \ +}; \ +bool TCONCAT(_Test_ignored_,name) = \ + ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); \ +void TCONCAT(_Test_,name)::_Run() + +// Register the specified test. Typically not used directly, but +// invoked via the macro expansion of TEST. +extern bool RegisterTest(const char* base, const char* name, void (*func)()); + + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ diff --git a/src/leveldb/util/testutil.cc b/src/leveldb/util/testutil.cc new file mode 100644 index 0000000000..538d09516d --- /dev/null +++ b/src/leveldb/util/testutil.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testutil.h" + +#include "util/random.h" + +namespace leveldb { +namespace test { + +Slice RandomString(Random* rnd, int len, std::string* dst) { + dst->resize(len); + for (int i = 0; i < len; i++) { + (*dst)[i] = static_cast<char>(' ' + rnd->Uniform(95)); // ' ' .. '~' + } + return Slice(*dst); +} + +std::string RandomKey(Random* rnd, int len) { + // Make sure to generate a wide variety of characters so we + // test the boundary conditions for short-key optimizations. + static const char kTestChars[] = { + '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff' + }; + std::string result; + for (int i = 0; i < len; i++) { + result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; + } + return result; +} + + +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + int len, std::string* dst) { + int raw = static_cast<int>(len * compressed_fraction); + if (raw < 1) raw = 1; + std::string raw_data; + RandomString(rnd, raw, &raw_data); + + // Duplicate the random data until we have filled "len" bytes + dst->clear(); + while (dst->size() < len) { + dst->append(raw_data); + } + dst->resize(len); + return Slice(*dst); +} + +} // namespace test +} // namespace leveldb diff --git a/src/leveldb/util/testutil.h b/src/leveldb/util/testutil.h new file mode 100644 index 0000000000..824e655bd2 --- /dev/null +++ b/src/leveldb/util/testutil.h @@ -0,0 +1,53 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_ +#define STORAGE_LEVELDB_UTIL_TESTUTIL_H_ + +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Store in *dst a random string of length "len" and return a Slice that +// references the generated data. +extern Slice RandomString(Random* rnd, int len, std::string* dst); + +// Return a random key with the specified length that may contain interesting +// characters (e.g. \x00, \xff, etc.). +extern std::string RandomKey(Random* rnd, int len); + +// Store in *dst a string of length "len" that will compress to +// "N*compressed_fraction" bytes and return a Slice that references +// the generated data. +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + int len, std::string* dst); + +// A wrapper that allows injection of errors. +class ErrorEnv : public EnvWrapper { + public: + bool writable_file_error_; + int num_writable_file_errors_; + + ErrorEnv() : EnvWrapper(Env::Default()), + writable_file_error_(false), + num_writable_file_errors_(0) { } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = NULL; + return Status::IOError(fname, "fake error"); + } + return target()->NewWritableFile(fname, result); + } +}; + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTUTIL_H_ diff --git a/src/leveldb/util/win_logger.cc b/src/leveldb/util/win_logger.cc new file mode 100644 index 0000000000..834c98cc76 --- /dev/null +++ b/src/leveldb/util/win_logger.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/win_logger.h" + +#include <windows.h> + +namespace leveldb { + +void WinLogger::Logv(const char* format, va_list ap) { + const uint64_t thread_id = static_cast<uint64_t>(::GetCurrentThreadId()); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + + char* p = base; + char* limit = base + bufsize; + + SYSTEMTIME st; + + // GetSystemTime returns UTC time, we want local time! + ::GetLocalTime(&st); + +#ifdef _MSC_VER + p += _snprintf_s(p, limit - p, _TRUNCATE, + "%04d/%02d/%02d-%02d:%02d:%02d.%03d %llx ", + st.wYear, + st.wMonth, + st.wDay, + st.wHour, + st.wMinute, + st.wSecond, + st.wMilliseconds, + static_cast<long long unsigned int>(thread_id)); +#else +#ifdef __MINGW32__ + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%03d %llx ", + st.wYear, + st.wMonth, + st.wDay, + st.wHour, + st.wMinute, + st.wSecond, + st.wMilliseconds, + static_cast<long long unsigned int>(thread_id)); +#else +#error Unable to detect Windows compiler (neither _MSC_VER nor __MINGW32__ are set) +#endif +#endif + + // Print the message + if (p < limit) { + va_list backup_ap = ap; + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + fwrite(base, 1, p - base, file_); + fflush(file_); + if (base != buffer) { + delete[] base; + } + break; + } +} + +}
\ No newline at end of file diff --git a/src/leveldb/util/win_logger.h b/src/leveldb/util/win_logger.h new file mode 100644 index 0000000000..b155d5c319 --- /dev/null +++ b/src/leveldb/util/win_logger.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// Logger implementation for Windows + +#ifndef STORAGE_LEVELDB_UTIL_WIN_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_WIN_LOGGER_H_ + +#include <stdio.h> +#include "leveldb/env.h" + +namespace leveldb { + +class WinLogger : public Logger { + private: + FILE* file_; + public: + explicit WinLogger(FILE* f) : file_(f) { assert(file_); } + virtual ~WinLogger() { + fclose(file_); + } + virtual void Logv(const char* format, va_list ap); + +}; + +} +#endif // STORAGE_LEVELDB_UTIL_WIN_LOGGER_H_ diff --git a/src/main.cpp b/src/main.cpp index da1072970c..064ac7e10d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "alert.h" #include "checkpoints.h" #include "db.h" +#include "txdb.h" #include "net.h" #include "init.h" #include "ui_interface.h" @@ -37,7 +38,9 @@ CBigNum bnBestChainWork = 0; CBigNum bnBestInvalidWork = 0; uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; +set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed int64 nTimeBestReceived = 0; +bool fImporting = false; CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have @@ -110,10 +113,10 @@ void static EraseFromWallets(uint256 hash) } // make sure all wallets know about the given transaction, in the given block -void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate) +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); + pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate); } // notify wallets about a new best chain @@ -159,6 +162,121 @@ void static ResendWalletTransactions() ////////////////////////////////////////////////////////////////////////////// // +// CCoinsView implementations +// + +bool CCoinsView::GetCoins(uint256 txid, CCoins &coins) { return false; } +bool CCoinsView::SetCoins(uint256 txid, const CCoins &coins) { return false; } +bool CCoinsView::HaveCoins(uint256 txid) { return false; } +CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } +bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } +bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; } +bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } + + +CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } +bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); } +bool CCoinsViewBacked::SetCoins(uint256 txid, const CCoins &coins) { return base->SetCoins(txid, coins); } +bool CCoinsViewBacked::HaveCoins(uint256 txid) { return base->HaveCoins(txid); } +CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } +bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } +void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } +bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } +bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } + +CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } + +bool CCoinsViewCache::GetCoins(uint256 txid, CCoins &coins) { + if (cacheCoins.count(txid)) { + coins = cacheCoins[txid]; + return true; + } + if (base->GetCoins(txid, coins)) { + cacheCoins[txid] = coins; + return true; + } + return false; +} + +std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(uint256 txid) { + std::map<uint256,CCoins>::iterator it = cacheCoins.find(txid); + if (it != cacheCoins.end()) + return it; + CCoins tmp; + if (!base->GetCoins(txid,tmp)) + return it; + std::pair<std::map<uint256,CCoins>::iterator,bool> ret = cacheCoins.insert(std::make_pair(txid, tmp)); + return ret.first; +} + +CCoins &CCoinsViewCache::GetCoins(uint256 txid) { + std::map<uint256,CCoins>::iterator it = FetchCoins(txid); + assert(it != cacheCoins.end()); + return it->second; +} + +bool CCoinsViewCache::SetCoins(uint256 txid, const CCoins &coins) { + cacheCoins[txid] = coins; + return true; +} + +bool CCoinsViewCache::HaveCoins(uint256 txid) { + return FetchCoins(txid) != cacheCoins.end(); +} + +CBlockIndex *CCoinsViewCache::GetBestBlock() { + if (pindexTip == NULL) + pindexTip = base->GetBestBlock(); + return pindexTip; +} + +bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) { + pindexTip = pindex; + return true; +} + +bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { + for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) + cacheCoins[it->first] = it->second; + pindexTip = pindex; + return true; +} + +bool CCoinsViewCache::Flush() { + bool fOk = base->BatchWrite(cacheCoins, pindexTip); + if (fOk) + cacheCoins.clear(); + return fOk; +} + +unsigned int CCoinsViewCache::GetCacheSize() { + return cacheCoins.size(); +} + +/** CCoinsView that brings transactions from a memorypool into view. + It does not check for spendings by memory pool transactions. */ +CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } + +bool CCoinsViewMemPool::GetCoins(uint256 txid, CCoins &coins) { + if (base->GetCoins(txid, coins)) + return true; + if (mempool.exists(txid)) { + const CTransaction &tx = mempool.lookup(txid); + coins = CCoins(tx, MEMPOOL_HEIGHT); + return true; + } + return false; +} + +bool CCoinsViewMemPool::HaveCoins(uint256 txid) { + return mempool.exists(txid) || base->HaveCoins(txid); +} + +CCoinsViewCache *pcoinsTip = NULL; +CBlockTreeDB *pblocktree = NULL; + +////////////////////////////////////////////////////////////////////////////// +// // mapOrphanTransactions // @@ -236,37 +354,9 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) ////////////////////////////////////////////////////////////////////////////// // -// CTransaction and CTxIndex +// CTransaction // -bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet) -{ - SetNull(); - if (!txdb.ReadTxIndex(prevout.hash, txindexRet)) - return false; - if (!ReadFromDisk(txindexRet.pos)) - return false; - if (prevout.n >= vout.size()) - { - SetNull(); - return false; - } - return true; -} - -bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout) -{ - CTxIndex txindex; - return ReadFromDisk(txdb, prevout, txindex); -} - -bool CTransaction::ReadFromDisk(COutPoint prevout) -{ - CTxDB txdb("r"); - CTxIndex txindex; - return ReadFromDisk(txdb, prevout, txindex); -} - bool CTransaction::IsStandard() const { if (nVersion > CTransaction::CURRENT_VERSION) @@ -302,7 +392,7 @@ bool CTransaction::IsStandard() const // expensive-to-check-upon-redemption script like: // DUP CHECKSIG DROP ... repeated 100 times... OP_1 // -bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const +bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const { if (IsCoinBase()) return true; // Coinbases don't use vin normally @@ -327,7 +417,7 @@ bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const // beside "push data" in the scriptSig the // IsStandard() call returns false vector<vector<unsigned char> > stack; - if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0)) + if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0)) return false; if (whichType == TX_SCRIPTHASH) @@ -382,17 +472,20 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) else { CBlock blockTmp; - if (pblock == NULL) - { - // Load the block this tx is in - CTxIndex txindex; - if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) - return 0; - if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) - return 0; - pblock = &blockTmp; + + if (pblock == NULL) { + CCoins coins; + if (pcoinsTip->GetCoins(GetHash(), coins)) { + CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); + if (pindex) { + if (!blockTmp.ReadFromDisk(pindex)) + return 0; + pblock = &blockTmp; + } + } } + if (pblock) { // Update the tx's hashBlock hashBlock = pblock->GetHash(); @@ -410,6 +503,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) // Fill in merkle branch vMerkleBranch = pblock->GetMerkleBranch(nIndex); + } } // Is the tx in a block that's in the main chain @@ -525,8 +619,20 @@ int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, return nMinFee; } +void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) +{ + LOCK(cs); + + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); + + // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx + while (it != mapNextTx.end() && it->first.hash == hashTx) { + coins.Spend(it->first.n); // and remove those outputs from coins + it++; + } +} -bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, +bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs) { if (pfMissingInputs) @@ -547,16 +653,13 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, if (!fTestNet && !tx.IsStandard()) return error("CTxMemPool::accept() : nonstandard transaction type"); - // Do we already have it? + // is it already in the memory pool? uint256 hash = tx.GetHash(); { LOCK(cs); if (mapTx.count(hash)) return false; } - if (fCheckInputs) - if (txdb.ContainsTx(hash)) - return false; // Check for conflicts with in-memory transactions CTransaction* ptxOld = NULL; @@ -588,27 +691,33 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, if (fCheckInputs) { - MapPrevTx mapInputs; - map<uint256, CTxIndex> mapUnused; - bool fInvalid = false; - if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) - { - if (fInvalid) - return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); - if (pfMissingInputs) - *pfMissingInputs = true; + CCoinsViewCache &view = *pcoinsTip; + + // do we already have it? + if (view.HaveCoins(hash)) return false; + + // do all inputs exist? + BOOST_FOREACH(const CTxIn txin, tx.vin) { + if (!view.HaveCoins(txin.prevout.hash)) { + if (pfMissingInputs) + *pfMissingInputs = true; + return false; + } } + if (!tx.HaveInputs(view)) + return error("CTxMemPool::accept() : inputs already spent"); + // Check for non-standard pay-to-script-hash in inputs - if (!tx.AreInputsStandard(mapInputs) && !fTestNet) + if (!tx.AreInputsStandard(view) && !fTestNet) return error("CTxMemPool::accept() : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. - int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + int64 nFees = tx.GetValueIn(view)-tx.GetValueOut(); unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block @@ -645,7 +754,7 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) + if (!tx.CheckInputs(view, CS_ALWAYS, true, false)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } @@ -673,9 +782,9 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, return true; } -bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) +bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs) { - return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs); + return mempool.accept(*this, fCheckInputs, pfMissingInputs); } bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) @@ -764,31 +873,24 @@ int CMerkleTx::GetBlocksToMaturity() const } -bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs) +bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs) { if (fClient) { - if (!IsInMainChain() && !ClientConnectInputs()) + if (!IsInMainChain() && !ClientCheckInputs()) return false; - return CTransaction::AcceptToMemoryPool(txdb, false); + return CTransaction::AcceptToMemoryPool(false); } else { - return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs); + return CTransaction::AcceptToMemoryPool(fCheckInputs); } } -bool CMerkleTx::AcceptToMemoryPool() -{ - CTxDB txdb("r"); - return AcceptToMemoryPool(txdb); -} - -bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) +bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs) { - { LOCK(mempool.cs); // Add previous supporting transactions first @@ -797,64 +899,59 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) if (!tx.IsCoinBase()) { uint256 hash = tx.GetHash(); - if (!mempool.exists(hash) && !txdb.ContainsTx(hash)) - tx.AcceptToMemoryPool(txdb, fCheckInputs); + if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) + tx.AcceptToMemoryPool(fCheckInputs); } } - return AcceptToMemoryPool(txdb, fCheckInputs); + return AcceptToMemoryPool(fCheckInputs); } return false; } -bool CWalletTx::AcceptWalletTransaction() -{ - CTxDB txdb("r"); - return AcceptWalletTransaction(txdb); -} - -int CTxIndex::GetDepthInMainChain() const -{ - // Read block header - CBlock block; - if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false)) - return 0; - // Find the block in the index - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash()); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) - return 0; - return 1 + nBestHeight - pindex->nHeight; -} // Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock -bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock) +bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) { + CBlockIndex *pindexSlow = NULL; { LOCK(cs_main); { LOCK(mempool.cs); if (mempool.exists(hash)) { - tx = mempool.lookup(hash); + txOut = mempool.lookup(hash); return true; } } - CTxDB txdb("r"); - CTxIndex txindex; - if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex)) - { - CBlock block; - if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) - hashBlock = block.GetHash(); - return true; + + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it + int nHeight = -1; + { + CCoinsViewCache &view = *pcoinsTip; + CCoins coins; + if (view.GetCoins(hash, coins)) + nHeight = coins.nHeight; + } + if (nHeight > 0) + pindexSlow = FindBlockByHeight(nHeight); } } - return false; -} + if (pindexSlow) { + CBlock block; + if (block.ReadFromDisk(pindexSlow)) { + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + if (tx.GetHash() == hash) { + txOut = tx; + hashBlock = pindexSlow->GetBlockHash(); + return true; + } + } + } + } + return false; +} @@ -891,7 +988,7 @@ bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) *this = pindex->GetBlockHeader(); return true; } - if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions)) + if (!ReadFromDisk(pindex->GetBlockPos(), fReadTransactions)) return false; if (GetHash() != pindex->GetBlockHash()) return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); @@ -1050,7 +1147,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) if (pindexNew->bnChainWork > bnBestInvalidWork) { bnBestInvalidWork = pindexNew->bnChainWork; - CTxDB().WriteBestInvalidWork(bnBestInvalidWork); + pblocktree->WriteBestInvalidWork(bnBestInvalidWork); uiInterface.NotifyBlocksChanged(); } printf("InvalidChainFound: invalid block=%s height=%d work=%s date=%s\n", @@ -1064,164 +1161,100 @@ void static InvalidChainFound(CBlockIndex* pindexNew) printf("InvalidChainFound: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); } -void CBlock::UpdateTime(const CBlockIndex* pindexPrev) -{ - nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - - // Updating time can change work required on testnet: - if (fTestNet) - nBits = GetNextWorkRequired(pindexPrev, this); +void static InvalidBlockFound(CBlockIndex *pindex) { + pindex->nStatus |= BLOCK_FAILED_VALID; + pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); + setBlockIndexValid.erase(pindex); + InvalidChainFound(pindex); + if (pindex->pnext) + ConnectBestBlock(); // reorganise away from the failed block } +bool ConnectBestBlock() { + do { + CBlockIndex *pindexNewBest; - - - - - - - - - -bool CTransaction::DisconnectInputs(CTxDB& txdb) -{ - // Relinquish previous transactions' spent pointers - if (!IsCoinBase()) - { - BOOST_FOREACH(const CTxIn& txin, vin) { - COutPoint prevout = txin.prevout; - - // Get prev txindex from disk - CTxIndex txindex; - if (!txdb.ReadTxIndex(prevout.hash, txindex)) - return error("DisconnectInputs() : ReadTxIndex failed"); + std::set<CBlockIndex*,CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexValid.rbegin(); + if (it == setBlockIndexValid.rend()) + return true; + pindexNewBest = *it; + } - if (prevout.n >= txindex.vSpent.size()) - return error("DisconnectInputs() : prevout.n out of range"); + if (pindexNewBest == pindexBest) + return true; // nothing to do + + // check ancestry + CBlockIndex *pindexTest = pindexNewBest; + std::vector<CBlockIndex*> vAttach; + do { + if (pindexTest->nStatus & BLOCK_FAILED_MASK) { + // mark descendants failed + CBlockIndex *pindexFailed = pindexNewBest; + while (pindexTest != pindexFailed) { + pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + setBlockIndexValid.erase(pindexFailed); + pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed)); + pindexFailed = pindexFailed->pprev; + } + InvalidChainFound(pindexNewBest); + break; + } - // Mark outpoint as not spent - txindex.vSpent[prevout.n].SetNull(); + if (pindexBest == NULL || pindexTest->bnChainWork > pindexBest->bnChainWork) + vAttach.push_back(pindexTest); - // Write back - if (!txdb.UpdateTxIndex(prevout.hash, txindex)) - return error("DisconnectInputs() : UpdateTxIndex failed"); - } - } + if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { + reverse(vAttach.begin(), vAttach.end()); + BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) + if (!SetBestChain(pindexSwitch)) + return false; + return true; + } + pindexTest = pindexTest->pprev; + } while(true); + } while(true); +} - // Remove transaction from index - // This can fail if a duplicate of this transaction was in a chain that got - // reorganized away. This is only possible if this transaction was completely - // spent, so erasing it would be a no-op anyway. - txdb.EraseTxIndex(*this); +void CBlock::UpdateTime(const CBlockIndex* pindexPrev) +{ + nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - return true; + // Updating time can change work required on testnet: + if (fTestNet) + nBits = GetNextWorkRequired(pindexPrev, this); } -bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool, - bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) -{ - // FetchInputs can return false either because we just haven't seen some inputs - // (in which case the transaction should be stored as an orphan) - // or because the transaction is malformed (in which case the transaction should - // be dropped). If tx is definitely invalid, fInvalid will be set to true. - fInvalid = false; - if (IsCoinBase()) - return true; // Coinbase transactions have no inputs to fetch. - for (unsigned int i = 0; i < vin.size(); i++) - { - COutPoint prevout = vin[i].prevout; - if (inputsRet.count(prevout.hash)) - continue; // Got it already - // Read txindex - CTxIndex& txindex = inputsRet[prevout.hash].first; - bool fFound = true; - if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) - { - // Get txindex from current proposed changes - txindex = mapTestPool.find(prevout.hash)->second; - } - else - { - // Read txindex from txdb - fFound = txdb.ReadTxIndex(prevout.hash, txindex); - } - if (!fFound && (fBlock || fMiner)) - return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - // Read txPrev - CTransaction& txPrev = inputsRet[prevout.hash].second; - if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) - { - // Get prev tx from single transactions in memory - { - LOCK(mempool.cs); - if (!mempool.exists(prevout.hash)) - return error("FetchInputs() : %s mempool Tx prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - txPrev = mempool.lookup(prevout.hash); - } - if (!fFound) - txindex.vSpent.resize(txPrev.vout.size()); - } - else - { - // Get prev tx from disk - if (!txPrev.ReadFromDisk(txindex.pos)) - return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - } - } - // Make sure all prevout.n indexes are valid: - for (unsigned int i = 0; i < vin.size(); i++) - { - const COutPoint prevout = vin[i].prevout; - assert(inputsRet.count(prevout.hash) != 0); - const CTxIndex& txindex = inputsRet[prevout.hash].first; - const CTransaction& txPrev = inputsRet[prevout.hash].second; - if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) - { - // Revisit this if/when transaction replacement is implemented and allows - // adding inputs: - fInvalid = true; - return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); - } - } - return true; -} -const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const -{ - MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash); - if (mi == inputs.end()) - throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found"); - const CTransaction& txPrev = (mi->second).second; - if (input.prevout.n >= txPrev.vout.size()) - throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range"); - return txPrev.vout[input.prevout.n]; +const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view) +{ + const CCoins &coins = view.GetCoins(input.prevout.hash); + assert(coins.IsAvailable(input.prevout.n)); + return coins.vout[input.prevout.n]; } -int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const +int64 CTransaction::GetValueIn(CCoinsViewCache& inputs) const { if (IsCoinBase()) return 0; int64 nResult = 0; for (unsigned int i = 0; i < vin.size(); i++) - { nResult += GetOutputFor(vin[i], inputs).nValue; - } - return nResult; + return nResult; } -unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const +unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const { if (IsCoinBase()) return 0; @@ -1229,107 +1262,126 @@ unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const unsigned int nSigOps = 0; for (unsigned int i = 0; i < vin.size(); i++) { - const CTxOut& prevout = GetOutputFor(vin[i], inputs); + const CTxOut &prevout = GetOutputFor(vin[i], inputs); if (prevout.scriptPubKey.IsPayToScriptHash()) nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); } return nSigOps; } -bool CTransaction::ConnectInputs(MapPrevTx inputs, - map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx, - const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash) +bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const +{ + // mark inputs spent + if (!IsCoinBase()) { + BOOST_FOREACH(const CTxIn &txin, vin) { + CCoins &coins = inputs.GetCoins(txin.prevout.hash); + CTxInUndo undo; + if (!coins.Spend(txin.prevout, undo)) + return error("UpdateCoins() : cannot spend input"); + txundo.vprevout.push_back(undo); + } + } + + // add outputs + if (!inputs.SetCoins(txhash, CCoins(*this, nHeight))) + return error("UpdateCoins() : cannot update output"); + + return true; +} + +bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const +{ + if (!IsCoinBase()) { + // first check whether information about the prevout hash is available + for (unsigned int i = 0; i < vin.size(); i++) { + const COutPoint &prevout = vin[i].prevout; + if (!inputs.HaveCoins(prevout.hash)) + return false; + } + + // then check whether the actual outputs are available + for (unsigned int i = 0; i < vin.size(); i++) { + const COutPoint &prevout = vin[i].prevout; + const CCoins &coins = inputs.GetCoins(prevout.hash); + if (!coins.IsAvailable(prevout.n)) + return false; + } + } + return true; +} + +bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings) const { - // Take over previous transactions' spent pointers - // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain - // fMiner is true when called from the internal bitcoin miner - // ... both are false when called from CTransaction::AcceptToMemoryPool if (!IsCoinBase()) { + // This doesn't trigger the DoS code on purpose; if it did, it would make it easier + // for an attacker to attempt to split the network. + if (!HaveInputs(inputs)) + return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str()); + + CBlockIndex *pindexBlock = inputs.GetBestBlock(); int64 nValueIn = 0; int64 nFees = 0; for (unsigned int i = 0; i < vin.size(); i++) { - COutPoint prevout = vin[i].prevout; - assert(inputs.count(prevout.hash) > 0); - CTxIndex& txindex = inputs[prevout.hash].first; - CTransaction& txPrev = inputs[prevout.hash].second; - - if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) - return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); + const COutPoint &prevout = vin[i].prevout; + const CCoins &coins = inputs.GetCoins(prevout.hash); // If prev is coinbase, check that it's matured - if (txPrev.IsCoinBase()) - for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) - if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) - return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); + if (coins.IsCoinBase()) { + if (pindexBlock->nHeight - coins.nHeight < COINBASE_MATURITY) + return error("CheckInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - coins.nHeight); + } // Check for negative or overflow input values - nValueIn += txPrev.vout[prevout.n].nValue; - if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return DoS(100, error("ConnectInputs() : txin values out of range")); + nValueIn += coins.vout[prevout.n].nValue; + if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return DoS(100, error("CheckInputs() : txin values out of range")); } + + if (nValueIn < GetValueOut()) + return DoS(100, error("ChecktInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); + + // Tally transaction fees + int64 nTxFee = nValueIn - GetValueOut(); + if (nTxFee < 0) + return DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); + nFees += nTxFee; + if (!MoneyRange(nFees)) + return DoS(100, error("CheckInputs() : nFees out of range")); + // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. - for (unsigned int i = 0; i < vin.size(); i++) - { - COutPoint prevout = vin[i].prevout; - assert(inputs.count(prevout.hash) > 0); - CTxIndex& txindex = inputs[prevout.hash].first; - CTransaction& txPrev = inputs[prevout.hash].second; - - // Check for conflicts (double-spend) - // This doesn't trigger the DoS code on purpose; if it did, it would make it easier - // for an attacker to attempt to split the network. - if (!txindex.vSpent[prevout.n].IsNull()) - return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); - - // Skip ECDSA signature verification when connecting blocks (fBlock=true) - // before the last blockchain checkpoint. This is safe because block merkle hashes are - // still computed and checked, and any change will be caught at the next checkpoint. - if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate()))) - { + + // Skip ECDSA signature verification when connecting blocks + // before the last blockchain checkpoint. This is safe because block merkle hashes are + // still computed and checked, and any change will be caught at the next checkpoint. + if (csmode == CS_ALWAYS || + (csmode == CS_AFTER_CHECKPOINT && inputs.GetBestBlock()->nHeight >= Checkpoints::GetTotalBlocksEstimate())) { + for (unsigned int i = 0; i < vin.size(); i++) { + const COutPoint &prevout = vin[i].prevout; + const CCoins &coins = inputs.GetCoins(prevout.hash); + // Verify signature - if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0)) - { + if (!VerifySignature(coins, *this, i, fStrictPayToScriptHash, fStrictEncodings, 0)) { // only during transition phase for P2SH: do not invoke anti-DoS code for // potentially old clients relaying bad P2SH transactions - if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0)) - return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); + if (fStrictPayToScriptHash && VerifySignature(coins, *this, i, false, fStrictEncodings, 0)) + return error("CheckInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); - return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + return DoS(100,error("CheckInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); } } - - // Mark outpoints as spent - txindex.vSpent[prevout.n] = posThisTx; - - // Write back - if (fBlock || fMiner) - { - mapTestPool[prevout.hash] = txindex; - } } - - if (nValueIn < GetValueOut()) - return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); - - // Tally transaction fees - int64 nTxFee = nValueIn - GetValueOut(); - if (nTxFee < 0) - return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); - nFees += nTxFee; - if (!MoneyRange(nFees)) - return DoS(100, error("ConnectInputs() : nFees out of range")); } return true; } -bool CTransaction::ClientConnectInputs() +bool CTransaction::ClientCheckInputs() const { if (IsCoinBase()) return false; @@ -1350,7 +1402,7 @@ bool CTransaction::ClientConnectInputs() return false; // Verify signature - if (!VerifySignature(txPrev, *this, i, true, 0)) + if (!VerifySignature(CCoins(txPrev, -1), *this, i, true, false, 0)) return error("ConnectInputs() : VerifySignature failed"); ///// this is redundant with the mempool.mapNextTx stuff, @@ -1378,32 +1430,105 @@ bool CTransaction::ClientConnectInputs() -bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) +bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) { - // Disconnect in reverse order - for (int i = vtx.size()-1; i >= 0; i--) - if (!vtx[i].DisconnectInputs(txdb)) - return false; + assert(pindex == view.GetBestBlock()); - // Update block index on disk without changing it in memory. - // The memory index structure will be changed after the db commits. - if (pindex->pprev) + CBlockUndo blockUndo; { - CDiskBlockIndex blockindexPrev(pindex->pprev); - blockindexPrev.hashNext = 0; - if (!txdb.WriteBlockIndex(blockindexPrev)) - return error("DisconnectBlock() : WriteBlockIndex failed"); + CDiskBlockPos pos = pindex->GetUndoPos(); + if (pos.IsNull()) + return error("DisconnectBlock() : no undo data available"); + FILE *file = OpenUndoFile(pos, true); + if (file == NULL) + return error("DisconnectBlock() : undo file not available"); + CAutoFile fileUndo(file, SER_DISK, CLIENT_VERSION); + fileUndo >> blockUndo; } + assert(blockUndo.vtxundo.size() + 1 == vtx.size()); + + // undo transactions in reverse order + for (int i = vtx.size() - 1; i >= 0; i--) { + const CTransaction &tx = vtx[i]; + uint256 hash = tx.GetHash(); + + // check that all outputs are available + if (!view.HaveCoins(hash)) + return error("DisconnectBlock() : outputs still spent? database corrupted"); + CCoins &outs = view.GetCoins(hash); + + CCoins outsBlock = CCoins(tx, pindex->nHeight); + if (outs != outsBlock) + return error("DisconnectBlock() : added transaction mismatch? database corrupted"); + + // remove outputs + outs = CCoins(); + + // restore inputs + if (i > 0) { // not coinbases + const CTxUndo &txundo = blockUndo.vtxundo[i-1]; + assert(txundo.vprevout.size() == tx.vin.size()); + for (unsigned int j = tx.vin.size(); j-- > 0;) { + const COutPoint &out = tx.vin[j].prevout; + const CTxInUndo &undo = txundo.vprevout[j]; + CCoins coins; + view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent + if (coins.IsPruned()) { + if (undo.nHeight == 0) + return error("DisconnectBlock() : undo data doesn't contain tx metadata? database corrupted"); + coins.fCoinBase = undo.fCoinBase; + coins.nHeight = undo.nHeight; + coins.nVersion = undo.nVersion; + } else { + if (undo.nHeight != 0) + return error("DisconnectBlock() : undo data contains unneeded tx metadata? database corrupted"); + } + if (coins.IsAvailable(out.n)) + return error("DisconnectBlock() : prevout output not spent? database corrupted"); + if (coins.vout.size() < out.n+1) + coins.vout.resize(out.n+1); + coins.vout[out.n] = undo.txout; + if (!view.SetCoins(out.hash, coins)) + return error("DisconnectBlock() : cannot restore coin inputs"); + } + } + } + + // move best block pointer to prevout block + view.SetBestBlock(pindex->pprev); + return true; } -bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) +void static FlushBlockFile() +{ + LOCK(cs_LastBlockFile); + + CDiskBlockPos posOld; + posOld.nFile = nLastBlockFile; + posOld.nPos = 0; + + FILE *fileOld = OpenBlockFile(posOld); + FileCommit(fileOld); + fclose(fileOld); + + fileOld = OpenUndoFile(posOld); + FileCommit(fileOld); + fclose(fileOld); +} + +bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize); + +bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) { // Check it again in case a previous version let a bad block in if (!CheckBlock(!fJustCheck, !fJustCheck)) return false; + // verify that the view's current state corresponds to the previous block + assert(pindex->pprev == view.GetBestBlock()); + // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. // If such overwrites are allowed, coinbases and transactions depending upon those @@ -1418,141 +1543,154 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) // initial block download. bool fEnforceBIP30 = !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); + if (fEnforceBIP30) { + for (unsigned int i=0; i<vtx.size(); i++) { + uint256 hash = GetTxHash(i); + if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) + return error("ConnectBlock() : tried to overwrite transaction"); + } + } // BIP16 didn't become active until Apr 1 2012 int64 nBIP16SwitchTime = 1333238400; bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime); - //// issue here: it doesn't know the version - unsigned int nTxPos; - if (fJustCheck) - // FetchInputs treats CDiskTxPos(1,1,1) as a special "refer to memorypool" indicator - // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from - nTxPos = 1; - else - nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size()); + CBlockUndo blockundo; - map<uint256, CTxIndex> mapQueuedChanges; int64 nFees = 0; unsigned int nSigOps = 0; - BOOST_FOREACH(CTransaction& tx, vtx) + for (unsigned int i=0; i<vtx.size(); i++) { - uint256 hashTx = tx.GetHash(); - - if (fEnforceBIP30) { - CTxIndex txindexOld; - if (txdb.ReadTxIndex(hashTx, txindexOld)) { - BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent) - if (pos.IsNull()) - return false; - } - } + const CTransaction &tx = vtx[i]; nSigOps += tx.GetLegacySigOpCount(); if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("ConnectBlock() : too many sigops")); - CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); - if (!fJustCheck) - nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); - - MapPrevTx mapInputs; if (!tx.IsCoinBase()) { - bool fInvalid; - if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) - return false; + if (!tx.HaveInputs(view)) + return DoS(100, error("ConnectBlock() : inputs missing/spent")); if (fStrictPayToScriptHash) { // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. - nSigOps += tx.GetP2SHSigOpCount(mapInputs); + nSigOps += tx.GetP2SHSigOpCount(view); if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("ConnectBlock() : too many sigops")); + return DoS(100, error("ConnectBlock() : too many sigops")); } - nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut(); + nFees += tx.GetValueIn(view)-tx.GetValueOut(); - if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash)) + if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash, false)) return false; } - mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size()); + CTxUndo txundo; + if (!tx.UpdateCoins(view, txundo, pindex->nHeight, GetTxHash(i))) + return error("ConnectBlock() : UpdateInputs failed"); + if (!tx.IsCoinBase()) + blockundo.vtxundo.push_back(txundo); } - if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) - return false; - if (fJustCheck) return true; - // Write queued txindex changes - for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) + // Write undo information to disk + if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) { - if (!txdb.UpdateTxIndex((*mi).first, (*mi).second)) - return error("ConnectBlock() : UpdateTxIndex failed"); - } + if (pindex->GetUndoPos().IsNull()) { + CDiskBlockPos pos; + if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8)) + return error("ConnectBlock() : FindUndoPos failed"); + if (!blockundo.WriteToDisk(pos)) + return error("ConnectBlock() : CBlockUndo::WriteToDisk failed"); + + // update nUndoPos in block index + pindex->nUndoPos = pos.nPos; + pindex->nStatus |= BLOCK_HAVE_UNDO; + } - // Update block index on disk without changing it in memory. - // The memory index structure will be changed after the db commits. - if (pindex->pprev) - { - CDiskBlockIndex blockindexPrev(pindex->pprev); - blockindexPrev.hashNext = pindex->GetBlockHash(); - if (!txdb.WriteBlockIndex(blockindexPrev)) + pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS; + + CDiskBlockIndex blockindex(pindex); + if (!pblocktree->WriteBlockIndex(blockindex)) return error("ConnectBlock() : WriteBlockIndex failed"); } + // add this block to the view's blockchain + if (!view.SetBestBlock(pindex)) + return false; + // Watch for transactions paying to me - BOOST_FOREACH(CTransaction& tx, vtx) - SyncWithWallets(tx, this, true); + for (unsigned int i=0; i<vtx.size(); i++) + SyncWithWallets(GetTxHash(i), vtx[i], this, true); return true; } -bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) +bool SetBestChain(CBlockIndex* pindexNew) { - printf("REORGANIZE\n"); + CCoinsViewCache &view = *pcoinsTip; + + // special case for attaching the genesis block + // note that no ConnectBlock is called, so its coinbase output is non-spendable + if (pindexGenesisBlock == NULL && pindexNew->GetBlockHash() == hashGenesisBlock) + { + view.SetBestBlock(pindexNew); + if (!view.Flush()) + return false; + pindexGenesisBlock = pindexNew; + pindexBest = pindexNew; + hashBestChain = pindexNew->GetBlockHash(); + nBestHeight = pindexBest->nHeight; + bnBestChainWork = pindexNew->bnChainWork; + return true; + } - // Find the fork - CBlockIndex* pfork = pindexBest; + // Find the fork (typically, there is none) + CBlockIndex* pfork = view.GetBestBlock(); CBlockIndex* plonger = pindexNew; while (pfork != plonger) { while (plonger->nHeight > pfork->nHeight) if (!(plonger = plonger->pprev)) - return error("Reorganize() : plonger->pprev is null"); + return error("SetBestChain() : plonger->pprev is null"); if (pfork == plonger) break; if (!(pfork = pfork->pprev)) - return error("Reorganize() : pfork->pprev is null"); + return error("SetBestChain() : pfork->pprev is null"); } - // List of what to disconnect + // List of what to disconnect (typically nothing) vector<CBlockIndex*> vDisconnect; - for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev) + for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev) vDisconnect.push_back(pindex); - // List of what to connect + // List of what to connect (typically only pindexNew) vector<CBlockIndex*> vConnect; for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) vConnect.push_back(pindex); reverse(vConnect.begin(), vConnect.end()); - printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str()); - printf("REORGANIZE: Connect %"PRIszu" blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str()); + if (vDisconnect.size() > 0) { + printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str()); + printf("REORGANIZE: Connect %"PRIszu" blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str()); + } // Disconnect shorter branch vector<CTransaction> vResurrect; - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) - { + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { CBlock block; if (!block.ReadFromDisk(pindex)) - return error("Reorganize() : ReadFromDisk for disconnect failed"); - if (!block.DisconnectBlock(txdb, pindex)) - return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); + return error("SetBestBlock() : ReadFromDisk for disconnect failed"); + CCoinsViewCache viewTemp(view, true); + if (!block.DisconnectBlock(pindex, viewTemp)) + return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); + if (!viewTemp.Flush()) + return error("SetBestBlock() : Cache flush failed after disconnect"); // Queue memory transactions to resurrect BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -1562,28 +1700,35 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) // Connect longer branch vector<CTransaction> vDelete; - for (unsigned int i = 0; i < vConnect.size(); i++) - { - CBlockIndex* pindex = vConnect[i]; + BOOST_FOREACH(CBlockIndex *pindex, vConnect) { CBlock block; if (!block.ReadFromDisk(pindex)) - return error("Reorganize() : ReadFromDisk for connect failed"); - if (!block.ConnectBlock(txdb, pindex)) - { - // Invalid block - return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); + return error("SetBestBlock() : ReadFromDisk for connect failed"); + CCoinsViewCache viewTemp(view, true); + if (!block.ConnectBlock(pindex, viewTemp)) { + InvalidChainFound(pindexNew); + InvalidBlockFound(pindex); + return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); } + if (!viewTemp.Flush()) + return error("SetBestBlock() : Cache flush failed after connect"); // Queue memory transactions to delete BOOST_FOREACH(const CTransaction& tx, block.vtx) vDelete.push_back(tx); } - if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) - return error("Reorganize() : WriteHashBestChain failed"); // Make sure it's successfully written to disk before changing memory structure - if (!txdb.TxnCommit()) - return error("Reorganize() : TxnCommit failed"); + bool fIsInitialDownload = IsInitialBlockDownload(); + if (!fIsInitialDownload || view.GetCacheSize()>5000) { + FlushBlockFile(); + pblocktree->Sync(); + if (!view.Flush()) + return false; + } + + // At this point, all changes have been done to the database. + // Proceed by updating the memory structures. // Disconnect shorter branch BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) @@ -1597,110 +1742,13 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) // Resurrect memory transactions that were in the disconnected branch BOOST_FOREACH(CTransaction& tx, vResurrect) - tx.AcceptToMemoryPool(txdb, false); + tx.AcceptToMemoryPool(false); // Delete redundant memory transactions that are in the connected branch BOOST_FOREACH(CTransaction& tx, vDelete) mempool.remove(tx); - printf("REORGANIZE: done\n"); - - return true; -} - - -// Called from inside SetBestChain: attaches a block to the new best chain being built -bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew) -{ - uint256 hash = GetHash(); - - // Adding to current best branch - if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) - { - txdb.TxnAbort(); - InvalidChainFound(pindexNew); - return false; - } - if (!txdb.TxnCommit()) - return error("SetBestChain() : TxnCommit failed"); - - // Add to current best branch - pindexNew->pprev->pnext = pindexNew; - - // Delete redundant memory transactions - BOOST_FOREACH(CTransaction& tx, vtx) - mempool.remove(tx); - - return true; -} - -bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) -{ - uint256 hash = GetHash(); - - if (!txdb.TxnBegin()) - return error("SetBestChain() : TxnBegin failed"); - - if (pindexGenesisBlock == NULL && hash == hashGenesisBlock) - { - txdb.WriteHashBestChain(hash); - if (!txdb.TxnCommit()) - return error("SetBestChain() : TxnCommit failed"); - pindexGenesisBlock = pindexNew; - } - else if (hashPrevBlock == hashBestChain) - { - if (!SetBestChainInner(txdb, pindexNew)) - return error("SetBestChain() : SetBestChainInner failed"); - } - else - { - // the first block in the new chain that will cause it to become the new best chain - CBlockIndex *pindexIntermediate = pindexNew; - - // list of blocks that need to be connected afterwards - std::vector<CBlockIndex*> vpindexSecondary; - - // Reorganize is costly in terms of db load, as it works in a single db transaction. - // Try to limit how much needs to be done inside - while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork) - { - vpindexSecondary.push_back(pindexIntermediate); - pindexIntermediate = pindexIntermediate->pprev; - } - - if (!vpindexSecondary.empty()) - printf("Postponing %"PRIszu" reconnects\n", vpindexSecondary.size()); - - // Switch to new best branch - if (!Reorganize(txdb, pindexIntermediate)) - { - txdb.TxnAbort(); - InvalidChainFound(pindexNew); - return error("SetBestChain() : Reorganize failed"); - } - - // Connect further blocks - BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary) - { - CBlock block; - if (!block.ReadFromDisk(pindex)) - { - printf("SetBestChain() : ReadFromDisk failed\n"); - break; - } - if (!txdb.TxnBegin()) { - printf("SetBestChain() : TxnBegin 2 failed\n"); - break; - } - // errors now are not fatal, we still did a reorganisation to a new chain in a valid way - if (!block.SetBestChainInner(txdb, pindex)) - break; - } - } - // Update best block in wallet (so we can detect restored wallets) - bool fIsInitialDownload = IsInitialBlockDownload(); if (!fIsInitialDownload) { const CBlockLocator locator(pindexNew); @@ -1708,15 +1756,15 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) } // New best block - hashBestChain = hash; + hashBestChain = pindexNew->GetBlockHash(); pindexBest = pindexNew; pblockindexFBBHLast = NULL; nBestHeight = pindexBest->nHeight; bnBestChainWork = pindexNew->bnChainWork; nTimeBestReceived = GetTime(); nTransactionsUpdated++; - printf("SetBestChain: new best=%s height=%d work=%s date=%s\n", - hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str(), + printf("SetBestChain: new best=%s height=%d work=%s tx=%lu date=%s\n", + hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str(), (unsigned long)pindexNew->nChainTx, DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); // Check the version of the last 100 blocks to see if we need to upgrade: @@ -1749,7 +1797,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) } -bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) +bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) { // Check for duplicate uint256 hash = GetHash(); @@ -1757,7 +1805,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str()); // Construct new block index object - CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this); + CBlockIndex* pindexNew = new CBlockIndex(*this); if (!pindexNew) return error("AddToBlockIndex() : new CBlockIndex failed"); map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; @@ -1768,35 +1816,110 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } + pindexNew->nTx = vtx.size(); pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork(); + pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx; + pindexNew->nFile = pos.nFile; + pindexNew->nDataPos = pos.nPos; + pindexNew->nUndoPos = 0; + pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; + setBlockIndexValid.insert(pindexNew); - CTxDB txdb; - if (!txdb.TxnBegin()) - return false; - txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); - if (!txdb.TxnCommit()) - return false; + pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)); - // New best - if (pindexNew->bnChainWork > bnBestChainWork) - if (!SetBestChain(txdb, pindexNew)) - return false; - - txdb.Close(); + // New best? + if (!ConnectBestBlock()) + return false; if (pindexNew == pindexBest) { // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; UpdatedTransaction(hashPrevBestCoinBase); - hashPrevBestCoinBase = vtx[0].GetHash(); + hashPrevBestCoinBase = GetTxHash(0); } + pblocktree->Flush(); + uiInterface.NotifyBlocksChanged(); return true; } +bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime) +{ + bool fUpdatedLast = false; + + LOCK(cs_LastBlockFile); + + while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); + FlushBlockFile(); + nLastBlockFile++; + infoLastBlockFile.SetNull(); + pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine + fUpdatedLast = true; + } + + pos.nFile = nLastBlockFile; + pos.nPos = infoLastBlockFile.nSize; + infoLastBlockFile.nSize += nAddSize; + infoLastBlockFile.AddBlock(nHeight, nTime); + + unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + FILE *file = OpenBlockFile(pos); + if (file) { + printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); + } + fclose(file); + } + + if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) + return error("FindBlockPos() : cannot write updated block info"); + if (fUpdatedLast) + pblocktree->WriteLastBlockFile(nLastBlockFile); + + return true; +} + +bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) +{ + pos.nFile = nFile; + + LOCK(cs_LastBlockFile); + + unsigned int nNewSize; + if (nFile == nLastBlockFile) { + pos.nPos = infoLastBlockFile.nUndoSize; + nNewSize = (infoLastBlockFile.nUndoSize += nAddSize); + if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) + return error("FindUndoPos() : cannot write updated block info"); + } else { + CBlockFileInfo info; + if (!pblocktree->ReadBlockFileInfo(nFile, info)) + return error("FindUndoPos() : cannot read block info"); + pos.nPos = info.nUndoSize; + nNewSize = (info.nUndoSize += nAddSize); + if (!pblocktree->WriteBlockFileInfo(nFile, info)) + return error("FindUndoPos() : cannot write updated block info"); + } + + unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; + unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + FILE *file = OpenUndoFile(pos); + if (file) { + printf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos); + } + fclose(file); + } + + return true; +} bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const @@ -1830,10 +1953,10 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: + BuildMerkleTree(); set<uint256> uniqueTx; - BOOST_FOREACH(const CTransaction& tx, vtx) - { - uniqueTx.insert(tx.GetHash()); + for (unsigned int i=0; i<vtx.size(); i++) { + uniqueTx.insert(GetTxHash(i)); } if (uniqueTx.size() != vtx.size()) return DoS(100, error("CheckBlock() : duplicate transaction")); @@ -1907,13 +2030,15 @@ bool CBlock::AcceptBlock() } // Write block to history file + unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) return error("AcceptBlock() : out of disk space"); - unsigned int nFile = -1; - unsigned int nBlockPos = 0; - if (!WriteToDisk(nFile, nBlockPos)) + CDiskBlockPos blockPos; + if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime)) + return error("AcceptBlock() : FindBlockPos failed"); + if (!WriteToDisk(blockPos)) return error("AcceptBlock() : WriteToDisk failed"); - if (!AddToBlockIndex(nFile, nBlockPos)) + if (!AddToBlockIndex(blockPos)) return error("AcceptBlock() : AddToBlockIndex failed"); // Relay inventory, but don't relay old inventory during initial block download @@ -2047,23 +2172,26 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) return true; } -static filesystem::path BlockFilePath(unsigned int nFile) -{ - string strBlockFn = strprintf("blk%04u.dat", nFile); - return GetDataDir() / strBlockFn; -} +CCriticalSection cs_LastBlockFile; +CBlockFileInfo infoLastBlockFile; +int nLastBlockFile = 0; -FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode) +FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) { - if ((nFile < 1) || (nFile == (unsigned int) -1)) + if (pos.IsNull()) return NULL; - FILE* file = fopen(BlockFilePath(nFile).string().c_str(), pszMode); - if (!file) + boost::filesystem::path path = GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); + boost::filesystem::create_directories(path.parent_path()); + FILE* file = fopen(path.string().c_str(), "rb+"); + if (!file && !fReadOnly) + file = fopen(path.string().c_str(), "wb+"); + if (!file) { + printf("Unable to open file %s\n", path.string().c_str()); return NULL; - if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) - { - if (fseek(file, nBlockPos, SEEK_SET) != 0) - { + } + if (pos.nPos) { + if (fseek(file, pos.nPos, SEEK_SET)) { + printf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str()); fclose(file); return NULL; } @@ -2071,27 +2199,122 @@ FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszM return file; } -static unsigned int nCurrentBlockFile = 1; +FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) { + return OpenDiskFile(pos, "blk", fReadOnly); +} + +FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { + return OpenDiskFile(pos, "rev", fReadOnly); +} -FILE* AppendBlockFile(unsigned int& nFileRet) +CBlockIndex * InsertBlockIndex(uint256 hash) { - nFileRet = 0; - loop + if (hash == 0) + return NULL; + + // Return existing + map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + return (*mi).second; + + // Create new + CBlockIndex* pindexNew = new CBlockIndex(); + if (!pindexNew) + throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); + mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + + return pindexNew; +} + +bool static LoadBlockIndexDB() +{ + if (!pblocktree->LoadBlockIndexGuts()) + return false; + + if (fRequestShutdown) + return true; + + // Calculate bnChainWork + vector<pair<int, CBlockIndex*> > vSortedByHeight; + vSortedByHeight.reserve(mapBlockIndex.size()); + BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { - FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab"); - if (!file) - return NULL; - if (fseek(file, 0, SEEK_END) != 0) - return NULL; - // FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB - if (ftell(file) < (long)(0x7F000000 - MAX_SIZE)) + CBlockIndex* pindex = item.second; + vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + } + sort(vSortedByHeight.begin(), vSortedByHeight.end()); + BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) + { + CBlockIndex* pindex = item.second; + pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); + pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK)) + setBlockIndexValid.insert(pindex); + } + + // Load block file info + pblocktree->ReadLastBlockFile(nLastBlockFile); + printf("LoadBlockIndex(): last block file = %i\n", nLastBlockFile); + if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile)) + printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str()); + + // Load hashBestChain pointer to end of best chain + pindexBest = pcoinsTip->GetBestBlock(); + if (pindexBest == NULL) + { + if (pindexGenesisBlock == NULL) + return true; + } + hashBestChain = pindexBest->GetBlockHash(); + nBestHeight = pindexBest->nHeight; + bnBestChainWork = pindexBest->bnChainWork; + + // set 'next' pointers in best chain + CBlockIndex *pindex = pindexBest; + while(pindex != NULL && pindex->pprev != NULL) { + CBlockIndex *pindexPrev = pindex->pprev; + pindexPrev->pnext = pindex; + pindex = pindexPrev; + } + printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n", + hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, + DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + + // Load bnBestInvalidWork, OK if it doesn't exist + pblocktree->ReadBestInvalidWork(bnBestInvalidWork); + + // Verify blocks in the best chain + int nCheckLevel = GetArg("-checklevel", 1); + int nCheckDepth = GetArg( "-checkblocks", 2500); + if (nCheckDepth == 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > nBestHeight) + nCheckDepth = nBestHeight; + printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); + CBlockIndex* pindexFork = NULL; + for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + { + if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) + break; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("LoadBlockIndex() : block.ReadFromDisk failed"); + // check level 1: verify block validity + if (nCheckLevel>0 && !block.CheckBlock()) { - nFileRet = nCurrentBlockFile; - return file; + printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexFork = pindex->pprev; } - fclose(file); - nCurrentBlockFile++; + // TODO: stronger verifications + } + if (pindexFork && !fRequestShutdown) + { + // TODO: reorg back + return error("LoadBlockIndex(): chain database corrupted"); } + + return true; } bool LoadBlockIndex(bool fAllowNew) @@ -2106,12 +2329,10 @@ bool LoadBlockIndex(bool fAllowNew) } // - // Load block index + // Load block index from databases // - CTxDB txdb("cr"); - if (!txdb.LoadBlockIndex()) + if (!LoadBlockIndexDB()) return false; - txdb.Close(); // // Init with genesis block @@ -2152,19 +2373,22 @@ bool LoadBlockIndex(bool fAllowNew) } //// debug print - printf("%s\n", block.GetHash().ToString().c_str()); + uint256 hash = block.GetHash(); + printf("%s\n", hash.ToString().c_str()); printf("%s\n", hashGenesisBlock.ToString().c_str()); printf("%s\n", block.hashMerkleRoot.ToString().c_str()); assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); block.print(); - assert(block.GetHash() == hashGenesisBlock); + assert(hash == hashGenesisBlock); // Start new block file - unsigned int nFile; - unsigned int nBlockPos; - if (!block.WriteToDisk(nFile, nBlockPos)) + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (!FindBlockPos(blockPos, nBlockSize+8, 0, block.nTime)) + return error("AcceptBlock() : FindBlockPos failed"); + if (!block.WriteToDisk(blockPos)) return error("LoadBlockIndex() : writing genesis block to disk failed"); - if (!block.AddToBlockIndex(nFile, nBlockPos)) + if (!block.AddToBlockIndex(blockPos)) return error("LoadBlockIndex() : genesis block not accepted"); } @@ -2218,11 +2442,9 @@ void PrintBlockTree() // print item CBlock block; block.ReadFromDisk(pindex); - printf("%d (%u,%u) %s %s tx %"PRIszu"", + printf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"", pindex->nHeight, - pindex->nFile, - pindex->nBlockPos, - block.GetHash().ToString().substr(0,20).c_str(), + pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos, DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), block.vtx.size()); @@ -2251,7 +2473,6 @@ bool LoadExternalBlockFile(FILE* fileIn) int nLoaded = 0; { - LOCK(cs_main); try { CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION); unsigned int nPos = 0; @@ -2288,6 +2509,7 @@ bool LoadExternalBlockFile(FILE* fileIn) { CBlock block; blkdat >> block; + LOCK(cs_main); if (ProcessBlock(NULL,&block)) { nLoaded++; @@ -2305,6 +2527,54 @@ bool LoadExternalBlockFile(FILE* fileIn) return nLoaded > 0; } +struct CImportingNow +{ + CImportingNow() { + assert(fImporting == false); + fImporting = true; + } + + ~CImportingNow() { + assert(fImporting == true); + fImporting = false; + } +}; + +void ThreadImport(void *data) { + std::vector<boost::filesystem::path> *vFiles = reinterpret_cast<std::vector<boost::filesystem::path>*>(data); + + RenameThread("bitcoin-loadblk"); + + CImportingNow imp; + vnThreadsRunning[THREAD_IMPORT]++; + + // -loadblock= + uiInterface.InitMessage(_("Starting block import...")); + BOOST_FOREACH(boost::filesystem::path &path, *vFiles) { + FILE *file = fopen(path.string().c_str(), "rb"); + if (file) + LoadExternalBlockFile(file); + } + + // hardcoded $DATADIR/bootstrap.dat + filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; + if (filesystem::exists(pathBootstrap)) { + uiInterface.InitMessage(_("Importing bootstrap blockchain data file.")); + + FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); + if (file) { + filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; + LoadExternalBlockFile(file); + RenameOver(pathBootstrap, pathBootstrapOld); + } + } + + delete vFiles; + + vnThreadsRunning[THREAD_IMPORT]--; +} + + @@ -2378,22 +2648,20 @@ string GetWarnings(string strFor) // -bool static AlreadyHave(CTxDB& txdb, const CInv& inv) +bool static AlreadyHave(const CInv& inv) { switch (inv.type) { case MSG_TX: { - bool txInMap = false; + bool txInMap = false; { - LOCK(mempool.cs); - txInMap = (mempool.exists(inv.hash)); + LOCK(mempool.cs); + txInMap = mempool.exists(inv.hash); } - return txInMap || - mapOrphanTransactions.count(inv.hash) || - txdb.ContainsTx(inv.hash); + return txInMap || mapOrphanTransactions.count(inv.hash) || + pcoinsTip->HaveCoins(inv.hash); } - case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); @@ -2512,7 +2780,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Ask the first connected node for block updates static int nAskedForBlocks = 0; - if (!pfrom->fClient && !pfrom->fOneShot && + if (!pfrom->fClient && !pfrom->fOneShot && !fImporting && + (pfrom->nStartingHeight > (nBestHeight - 144)) && (pfrom->nVersion < NOBLKS_VERSION_START || pfrom->nVersion >= NOBLKS_VERSION_END) && (nAskedForBlocks < 1 || vNodes.size() <= 1)) @@ -2635,7 +2904,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) break; } } - CTxDB txdb("r"); for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; @@ -2644,13 +2912,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; pfrom->AddInventoryKnown(inv); - bool fAlreadyHave = AlreadyHave(txdb, inv); + bool fAlreadyHave = AlreadyHave(inv); if (fDebug) printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); - if (!fAlreadyHave) - pfrom->AskFor(inv); - else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { + if (!fAlreadyHave) { + if (!fImporting) + pfrom->AskFor(inv); + } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); } else if (nInv == nLastBlock) { // In case we are on a very long side-chain, it is possible that we already have @@ -2815,7 +3084,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector<uint256> vWorkQueue; vector<uint256> vEraseQueue; CDataStream vMsg(vRecv); - CTxDB txdb("r"); CTransaction tx; vRecv >> tx; @@ -2823,9 +3091,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); bool fMissingInputs = false; - if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs)) + if (tx.AcceptToMemoryPool(true, &fMissingInputs)) { - SyncWithWallets(tx, NULL, true); + SyncWithWallets(inv.hash, tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -2845,10 +3113,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CInv inv(MSG_TX, tx.GetHash()); bool fMissingInputs2 = false; - if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs2)) + if (tx.AcceptToMemoryPool(true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); - SyncWithWallets(tx, NULL, true); + SyncWithWallets(inv.hash, tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -3293,11 +3561,10 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // vector<CInv> vGetData; int64 nNow = GetTime() * 1000000; - CTxDB txdb("r"); while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { const CInv& inv = (*pto->mapAskFor.begin()).second; - if (!AlreadyHave(txdb, inv)) + if (!AlreadyHave(inv)) { if (fDebugNet) printf("sending getdata: %s\n", inv.ToString().c_str()); @@ -3507,7 +3774,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) int64 nFees = 0; { LOCK2(cs_main, mempool.cs); - CTxDB txdb("r"); + CCoinsViewCache view(*pcoinsTip, true); // Priority order to process transactions list<COrphan> vOrphan; // list memory doesn't move @@ -3529,9 +3796,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Read prev transaction - CTransaction txPrev; - CTxIndex txindex; - if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + CCoins coins; + if (!view.GetCoins(txin.prevout.hash, coins)) { // This should never happen; all transactions in the memory // pool should connect to either transactions in the chain @@ -3558,10 +3824,12 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue; continue; } - int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; + + int64 nValueIn = coins.vout[txin.prevout.n].nValue; nTotalIn += nValueIn; - int nConf = txindex.GetDepthInMainChain(); + int nConf = pindexPrev->nHeight - coins.nHeight; + dPriority += (double)nValueIn * nConf; } if (fMissingInputs) continue; @@ -3585,7 +3853,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) } // Collect transactions into block - map<uint256, CTxIndex> mapTestPool; uint64 nBlockSize = 1000; uint64 nBlockTx = 0; int nBlockSigOps = 100; @@ -3604,6 +3871,9 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); vecPriority.pop_back(); + // second layer cached modifications just for this transaction + CCoinsViewCache viewTemp(view, true); + // Size limits unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); if (nBlockSize + nTxSize >= nBlockMaxSize) @@ -3628,24 +3898,25 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); } - // Connecting shouldn't fail due to dependency on other memory pool transactions - // because we're already processing them in order of dependency - map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool); - MapPrevTx mapInputs; - bool fInvalid; - if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid)) + if (!tx.HaveInputs(viewTemp)) continue; - int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + int64 nTxFees = tx.GetValueIn(viewTemp)-tx.GetValueOut(); - nTxSigOps += tx.GetP2SHSigOpCount(mapInputs); + nTxSigOps += tx.GetP2SHSigOpCount(viewTemp); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; - if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true)) + if (!tx.CheckInputs(viewTemp, CS_ALWAYS, true, false)) + continue; + + CTxUndo txundo; + uint256 hash = tx.GetHash(); + if (!tx.UpdateCoins(viewTemp, txundo, pindexPrev->nHeight+1, hash)) continue; - mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size()); - swap(mapTestPool, mapTestPoolTmp); + + // push changes from the second layer cache to the first one + viewTemp.Flush(); // Added pblock->vtx.push_back(tx); @@ -3661,7 +3932,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) } // Add transactions that depend on this one to the priority queue - uint256 hash = tx.GetHash(); if (mapDependers.count(hash)) { BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) @@ -3683,19 +3953,20 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) nLastBlockSize = nBlockSize; printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); - pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); - - // Fill in header - pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - pblock->UpdateTime(pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); - pblock->nNonce = 0; + pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + pblock->UpdateTime(pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); + pblock->nNonce = 0; pblock->vtx[0].vin[0].scriptSig = scriptDummy; - CBlockIndex indexDummy(1, 1, *pblock); + + CBlockIndex indexDummy(*pblock); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; - if (!pblock->ConnectBlock(txdb, &indexDummy, true)) + CCoinsViewCache viewNew(*pcoinsTip, true); + if (!pblock->ConnectBlock(&indexDummy, viewNew, true)) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); } @@ -4007,3 +4278,57 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet) } } } + +// Amount compression: +// * If the amount is 0, output 0 +// * first, divide the amount (in base units) by the largest power of 10 possible; call the exponent e (e is max 9) +// * if e<9, the last digit of the resulting number cannot be 0; store it as d, and drop it (divide by 10) +// * call the result n +// * output 1 + 10*(9*n + d - 1) + e +// * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9 +// (this is decodable, as d is in [1-9] and e is in [0-9]) + +uint64 CTxOutCompressor::CompressAmount(uint64 n) +{ + if (n == 0) + return 0; + int e = 0; + while (((n % 10) == 0) && e < 9) { + n /= 10; + e++; + } + if (e < 9) { + int d = (n % 10); + assert(d >= 1 && d <= 9); + n /= 10; + return 1 + (n*9 + d - 1)*10 + e; + } else { + return 1 + (n - 1)*10 + 9; + } +} + +uint64 CTxOutCompressor::DecompressAmount(uint64 x) +{ + // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9 + if (x == 0) + return 0; + x--; + // x = 10*(9*n + d - 1) + e + int e = x % 10; + x /= 10; + uint64 n = 0; + if (e < 9) { + // x = 9*n + d - 1 + int d = (x % 9) + 1; + x /= 9; + // x = n + n = x*10 + d; + } else { + n = x+1; + } + while (e) { + n *= 10; + e--; + } + return n; +} diff --git a/src/main.h b/src/main.h index 68302b85ac..75ee7a9be5 100644 --- a/src/main.h +++ b/src/main.h @@ -23,11 +23,17 @@ class CInv; class CRequestTracker; class CNode; +class CBlockIndexWorkComparator; + static const unsigned int MAX_BLOCK_SIZE = 1000000; static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; static const unsigned int MAX_INV_SZ = 50000; +static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB +static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB +static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; static const int64 MIN_TX_FEE = 50000; static const int64 MIN_RELAY_TX_FEE = 10000; static const int64 MAX_MONEY = 21000000 * COIN; @@ -51,6 +57,7 @@ extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern std::map<uint256, CBlockIndex*> mapBlockIndex; +extern std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; extern uint256 hashGenesisBlock; extern CBlockIndex* pindexGenesisBlock; extern int nBestHeight; @@ -68,6 +75,7 @@ extern int64 nTimeBestReceived; extern CCriticalSection cs_setpwalletRegistered; extern std::set<CWallet*> setpwalletRegistered; extern unsigned char pchMessageStart[4]; +extern bool fImporting; // Settings extern int64 nTransactionFee; @@ -77,22 +85,27 @@ static const uint64 nMinDiskSpace = 52428800; class CReserveKey; -class CTxDB; -class CTxIndex; +class CCoinsDB; +class CBlockTreeDB; +class CDiskBlockPos; +class CCoins; +class CTxUndo; +class CCoinsView; +class CCoinsViewCache; void RegisterWallet(CWallet* pwalletIn); void UnregisterWallet(CWallet* pwalletIn); -void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); bool ProcessBlock(CNode* pfrom, CBlock* pblock); bool CheckDiskSpace(uint64 nAdditionalBytes=0); -FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); -FILE* AppendBlockFile(unsigned int& nFileRet); +FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); +FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); bool LoadBlockIndex(bool fAllowNew=true); void PrintBlockTree(); CBlockIndex* FindBlockByHeight(int nHeight); bool ProcessMessages(CNode* pfrom); bool SendMessages(CNode* pto, bool fSendTrickle); -bool LoadExternalBlockFile(FILE* fileIn); +void ThreadImport(void *parg); void GenerateBitcoins(bool fGenerate, CWallet* pwallet); CBlock* CreateNewBlock(CReserveKey& reservekey); void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); @@ -103,7 +116,10 @@ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); int GetNumBlocksOfPeers(); bool IsInitialBlockDownload(); std::string GetWarnings(std::string strFor); -bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock); +bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); +bool SetBestChain(CBlockIndex* pindexNew); +bool ConnectBestBlock(); +CBlockIndex * InsertBlockIndex(uint256 hash); @@ -117,58 +133,32 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock); bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); -/** Position on disk for a particular transaction. */ -class CDiskTxPos +class CDiskBlockPos { public: - unsigned int nFile; - unsigned int nBlockPos; - unsigned int nTxPos; - - CDiskTxPos() - { - SetNull(); - } - - CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn) - { - nFile = nFileIn; - nBlockPos = nBlockPosIn; - nTxPos = nTxPosIn; - } + int nFile; + unsigned int nPos; - IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) - void SetNull() { nFile = (unsigned int) -1; nBlockPos = 0; nTxPos = 0; } - bool IsNull() const { return (nFile == (unsigned int) -1); } + IMPLEMENT_SERIALIZE( + READWRITE(VARINT(nFile)); + READWRITE(VARINT(nPos)); + ) - friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) - { - return (a.nFile == b.nFile && - a.nBlockPos == b.nBlockPos && - a.nTxPos == b.nTxPos); + friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) { + return (a.nFile == b.nFile && a.nPos == b.nPos); } - friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) - { + friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) { return !(a == b); } - std::string ToString() const - { - if (IsNull()) - return "null"; - else - return strprintf("(nFile=%u, nBlockPos=%u, nTxPos=%u)", nFile, nBlockPos, nTxPos); - } - - void print() const - { - printf("%s", ToString().c_str()); - } + void SetNull() { nFile = -1; nPos = 0; } + bool IsNull() const { return (nFile == -1); } }; + /** An inpoint - a combination of a transaction and an index n into its vin */ class CInPoint { @@ -336,7 +326,7 @@ public: scriptPubKey.clear(); } - bool IsNull() + bool IsNull() const { return (nValue == -1); } @@ -380,7 +370,13 @@ enum GetMinFee_mode GMF_SEND, }; -typedef std::map<uint256, std::pair<CTxIndex, CTransaction> > MapPrevTx; +// Modes for script/signature checking +enum CheckSig_mode +{ + CS_NEVER, // never validate scripts + CS_AFTER_CHECKPOINT, // validate scripts after the last checkpoint + CS_ALWAYS // always validate scripts +}; /** The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. @@ -492,7 +488,7 @@ public: @return True if all inputs (scriptSigs) use only standard transaction forms @see CTransaction::FetchInputs */ - bool AreInputsStandard(const MapPrevTx& mapInputs) const; + bool AreInputsStandard(CCoinsViewCache& mapInputs) const; /** Count ECDSA signature operations the old-fashioned (pre-0.6) way @return number of sigops this transaction's outputs will produce when spent @@ -506,7 +502,7 @@ public: @return maximum number of sigops required to validate this transaction's inputs @see CTransaction::FetchInputs */ - unsigned int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const; + unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const; /** Amount of bitcoins spent by this transaction. @return sum of all outputs (note: does not include fees) @@ -531,7 +527,7 @@ public: @return Sum of value of all inputs (scriptSigs) @see CTransaction::FetchInputs */ - int64 GetValueIn(const MapPrevTx& mapInputs) const; + int64 GetValueIn(CCoinsViewCache& mapInputs) const; static bool AllowFree(double dPriority) { @@ -542,33 +538,6 @@ public: int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const; - bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) - { - CAutoFile filein = CAutoFile(OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION); - if (!filein) - return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); - - // Read transaction - if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) - return error("CTransaction::ReadFromDisk() : fseek failed"); - - try { - filein >> *this; - } - catch (std::exception &e) { - return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); - } - - // Return file pointer - if (pfileRet) - { - if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) - return error("CTransaction::ReadFromDisk() : second fseek failed"); - *pfileRet = filein.release(); - } - return true; - } - friend bool operator==(const CTransaction& a, const CTransaction& b) { return (a.nVersion == b.nVersion && @@ -605,48 +574,388 @@ public: } - bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet); - bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); - bool ReadFromDisk(COutPoint prevout); - bool DisconnectInputs(CTxDB& txdb); + // Do all possible client-mode checks + bool ClientCheckInputs() const; - /** Fetch from memory and/or disk. inputsRet keys are transaction hashes. + // Check whether all prevouts of this transaction are present in the UTXO set represented by view + bool HaveInputs(CCoinsViewCache &view) const; - @param[in] txdb Transaction database - @param[in] mapTestPool List of pending changes to the transaction index database - @param[in] fBlock True if being called to add a new best-block to the chain - @param[in] fMiner True if being called by CreateNewBlock - @param[out] inputsRet Pointers to this transaction's inputs - @param[out] fInvalid returns true if transaction is invalid - @return Returns true if all inputs are in txdb or mapTestPool - */ - bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool, - bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid); - - /** Sanity check previous transactions, then, if all checks succeed, - mark them as spent by this transaction. - - @param[in] inputs Previous transactions (from FetchInputs) - @param[out] mapTestPool Keeps track of inputs that need to be updated on disk - @param[in] posThisTx Position of this transaction on disk - @param[in] pindexBlock - @param[in] fBlock true if called from ConnectBlock - @param[in] fMiner true if called from CreateNewBlock - @param[in] fStrictPayToScriptHash true if fully validating p2sh transactions - @return Returns true if all checks succeed - */ - bool ConnectInputs(MapPrevTx inputs, - std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx, - const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true); - bool ClientConnectInputs(); + // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) + // This does not modify the UTXO set + bool CheckInputs(CCoinsViewCache &view, enum CheckSig_mode csmode, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true) const; + + // Apply the effects of this transaction on the UTXO set represented by view + bool UpdateCoins(CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; + + // Context-independent validity checks bool CheckTransaction() const; - bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); + + // Try to accept this transaction into the memory pool + bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL); protected: - const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const; + static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); }; +/** wrapper for CTxOut that provides a more compact serialization */ +class CTxOutCompressor +{ +private: + CTxOut &txout; + +public: + static uint64 CompressAmount(uint64 nAmount); + static uint64 DecompressAmount(uint64 nAmount); + + CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } + + IMPLEMENT_SERIALIZE(({ + if (!fRead) { + uint64 nVal = CompressAmount(txout.nValue); + READWRITE(VARINT(nVal)); + } else { + uint64 nVal = 0; + READWRITE(VARINT(nVal)); + txout.nValue = DecompressAmount(nVal); + } + CScriptCompressor cscript(REF(txout.scriptPubKey)); + READWRITE(cscript); + });) +}; +/** Undo information for a CTxIn + * + * Contains the prevout's CTxOut being spent, and if this was the + * last output of the affected transaction, its metadata as well + * (coinbase or not, height, transaction version) + */ +class CTxInUndo +{ +public: + CTxOut txout; // the txout data before being spent + bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase + unsigned int nHeight; // if the outpoint was the last unspent: its height + int nVersion; // if the outpoint was the last unspent: its version + + CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {} + CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) + + (nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) + + ::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template<typename Stream> + void Serialize(Stream &s, int nType, int nVersion) const { + ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion); + if (nHeight > 0) + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + ::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template<typename Stream> + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + ::Unserialize(s, VARINT(nCode), nType, nVersion); + nHeight = nCode / 2; + fCoinBase = nCode & 1; + if (nHeight > 0) + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + ::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion); + } +}; + +/** Undo information for a CTransaction */ +class CTxUndo +{ +public: + // undo information for all txins + std::vector<CTxInUndo> vprevout; + + IMPLEMENT_SERIALIZE( + READWRITE(vprevout); + ) +}; + +/** Undo information for a CBlock */ +class CBlockUndo +{ +public: + std::vector<CTxUndo> vtxundo; // for all but the coinbase + + IMPLEMENT_SERIALIZE( + READWRITE(vtxundo); + ) + + bool WriteToDisk(CDiskBlockPos &pos) + { + + // Open history file to append + CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CBlockUndo::WriteToDisk() : OpenUndoFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(pchMessageStart) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) + return error("CBlock::WriteToDisk() : ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << *this; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload()) + FileCommit(fileout); + + return true; + } + +}; + +/** pruned version of CTransaction: only retains metadata and unspent transaction outputs + * + * Serialized format: + * - VARINT(nVersion) + * - VARINT(nCode) + * - unspentness bitvector, for vout[2] and further; least significant byte first + * - the non-spent CTxOuts (via CTxOutCompressor) + * - VARINT(nHeight) + * + * The nCode value consists of: + * - bit 1: IsCoinBase() + * - bit 2: vout[0] is not spent + * - bit 4: vout[1] is not spent + * - The higher bits encode N, the number of non-zero bytes in the following bitvector. + * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at + * least one non-spent output). + * + * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e + * <><><--------------------------------------------><----> + * | \ | / + * version code vout[1] height + * + * - version = 1 + * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) + * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 + * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 + * * 8358: compact amount representation for 60000000000 (600 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 + * - height = 203998 + * + * + * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b + * <><><--><--------------------------------------------------><----------------------------------------------><----> + * / \ \ | | / + * version code unspentness vout[4] vout[16] height + * + * - version = 1 + * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, + * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) + * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent + * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee + * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 + * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 + * * bbd123: compact amount representation for 110397 (0.001 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 + * - height = 120891 + */ +class CCoins +{ +public: + // whether transaction is a coinbase + bool fCoinBase; + + // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped + std::vector<CTxOut> vout; + + // at which height this transaction was included in the active blockchain + int nHeight; + + // version of the CTransaction; accesses to this value should probably check for nHeight as well, + // as new tx version will probably only be introduced at certain heights + int nVersion; + + // construct a CCoins from a CTransaction, at a given height + CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { } + + // empty constructor + CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } + + // remove spent outputs at the end of vout + void Cleanup() { + while (vout.size() > 0 && vout.back().IsNull()) + vout.pop_back(); + } + + // equality test + friend bool operator==(const CCoins &a, const CCoins &b) { + return a.fCoinBase == b.fCoinBase && + a.nHeight == b.nHeight && + a.nVersion == b.nVersion && + a.vout == b.vout; + } + friend bool operator!=(const CCoins &a, const CCoins &b) { + return !(a == b); + } + + // calculate number of bytes for the bitmask, and its number of non-zero bytes + // each bit in the bitmask represents the availability of one output, but the + // availabilities of the first two outputs are encoded separately + void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { + unsigned int nLastUsedByte = 0; + for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { + bool fZero = true; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { + if (!vout[2+b*8+i].IsNull()) { + fZero = false; + continue; + } + } + if (!fZero) { + nLastUsedByte = b + 1; + nNonzeroBytes++; + } + } + nBytes += nLastUsedByte; + } + + bool IsCoinBase() const { + return fCoinBase; + } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + unsigned int nSize = 0; + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); + // size of header code + nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); + // spentness bitmask + nSize += nMaskSize; + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) + if (!vout[i].IsNull()) + nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); + // height + nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); + return nSize; + } + + template<typename Stream> + void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Serialize(s, VARINT(nCode), nType, nVersion); + // spentness bitmask + for (unsigned int b = 0; b<nMaskSize; b++) { + unsigned char chAvail = 0; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) + if (!vout[2+b*8+i].IsNull()) + chAvail |= (1 << i); + ::Serialize(s, chAvail, nType, nVersion); + } + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) { + if (!vout[i].IsNull()) + ::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion); + } + // coinbase height + ::Serialize(s, VARINT(nHeight), nType, nVersion); + } + + template<typename Stream> + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + // version + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Unserialize(s, VARINT(nCode), nType, nVersion); + fCoinBase = nCode & 1; + std::vector<bool> vAvail(2, false); + vAvail[0] = nCode & 2; + vAvail[1] = nCode & 4; + unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); + // spentness bitmask + while (nMaskCode > 0) { + unsigned char chAvail = 0; + ::Unserialize(s, chAvail, nType, nVersion); + for (unsigned int p = 0; p < 8; p++) { + bool f = (chAvail & (1 << p)) != 0; + vAvail.push_back(f); + } + if (chAvail != 0) + nMaskCode--; + } + // txouts themself + vout.assign(vAvail.size(), CTxOut()); + for (unsigned int i = 0; i < vAvail.size(); i++) { + if (vAvail[i]) + ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); + } + // coinbase height + ::Unserialize(s, VARINT(nHeight), nType, nVersion); + Cleanup(); + } + + // mark an outpoint spent, and construct undo information + bool Spend(const COutPoint &out, CTxInUndo &undo) { + if (out.n >= vout.size()) + return false; + if (vout[out.n].IsNull()) + return false; + undo = CTxInUndo(vout[out.n]); + vout[out.n].SetNull(); + Cleanup(); + if (vout.size() == 0) { + undo.nHeight = nHeight; + undo.fCoinBase = fCoinBase; + undo.nVersion = this->nVersion; + } + return true; + } + + // mark a vout spent + bool Spend(int nPos) { + CTxInUndo undo; + COutPoint out(0, nPos); + return Spend(out, undo); + } + + // check whether a particular output is still available + bool IsAvailable(unsigned int nPos) const { + return (nPos < vout.size() && !vout[nPos].IsNull()); + } + + // check whether the entire CCoins is spent + // note that only !IsPruned() CCoins can be serialized + bool IsPruned() const { + BOOST_FOREACH(const CTxOut &out, vout) + if (!out.IsNull()) + return false; + return true; + } +}; @@ -695,66 +1004,15 @@ public: int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } bool IsInMainChain() const { return GetDepthInMainChain() > 0; } int GetBlocksToMaturity() const; - bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true); - bool AcceptToMemoryPool(); + bool AcceptToMemoryPool(bool fCheckInputs=true); }; -/** A txdb record that contains the disk location of a transaction and the - * locations of transactions that spend its outputs. vSpent is really only - * used as a flag, but having the location is very helpful for debugging. - */ -class CTxIndex -{ -public: - CDiskTxPos pos; - std::vector<CDiskTxPos> vSpent; - CTxIndex() - { - SetNull(); - } - CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs) - { - pos = posIn; - vSpent.resize(nOutputs); - } - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(pos); - READWRITE(vSpent); - ) - - void SetNull() - { - pos.SetNull(); - vSpent.clear(); - } - - bool IsNull() - { - return pos.IsNull(); - } - - friend bool operator==(const CTxIndex& a, const CTxIndex& b) - { - return (a.pos == b.pos && - a.vSpent == b.vSpent); - } - - friend bool operator!=(const CTxIndex& a, const CTxIndex& b) - { - return !(a == b); - } - int GetDepthInMainChain() const; - -}; @@ -766,9 +1024,6 @@ public: * to everyone and the block is added to the block chain. The first transaction * in the block is a special one that creates a new coin owned by the creator * of the block. - * - * Blocks are appended to blk0001.dat files on disk. Their location on disk - * is indexed by CBlockIndex objects in memory. */ class CBlock { @@ -864,6 +1119,12 @@ public: return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); } + const uint256 &GetTxHash(unsigned int nIndex) const { + assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first + assert(nIndex < vtx.size()); + return vMerkleTree[nIndex]; + } + std::vector<uint256> GetMerkleBranch(int nIndex) const { if (vMerkleTree.empty()) @@ -896,12 +1157,12 @@ public: } - bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) + bool WriteToDisk(CDiskBlockPos &pos) { // Open history file to append - CAutoFile fileout = CAutoFile(AppendBlockFile(nFileRet), SER_DISK, CLIENT_VERSION); + CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); if (!fileout) - return error("CBlock::WriteToDisk() : AppendBlockFile failed"); + return error("CBlock::WriteToDisk() : OpenBlockFile failed"); // Write index header unsigned int nSize = fileout.GetSerializeSize(*this); @@ -911,23 +1172,23 @@ public: long fileOutPos = ftell(fileout); if (fileOutPos < 0) return error("CBlock::WriteToDisk() : ftell failed"); - nBlockPosRet = fileOutPos; + pos.nPos = (unsigned int)fileOutPos; fileout << *this; // Flush stdio buffers and commit to disk before returning fflush(fileout); - if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0) + if (!IsInitialBlockDownload()) FileCommit(fileout); return true; } - bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) + bool ReadFromDisk(const CDiskBlockPos &pos, bool fReadTransactions = true) { SetNull(); // Open history file to read - CAutoFile filein = CAutoFile(OpenBlockFile(nFile, nBlockPos, "rb"), SER_DISK, CLIENT_VERSION); + CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (!filein) return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); if (!fReadTransactions) @@ -971,22 +1232,103 @@ public: } - bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); - bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false); + // Undo the effects of this block (with given index) on the UTXO set represented by coins + bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins); + + // Apply the effects of this block (with given index) on the UTXO set represented by coins + bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); + + // Read a block from disk bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); - bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); - bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); + + // Add this block to the block index, and if necessary, switch the active block chain to this + bool AddToBlockIndex(const CDiskBlockPos &pos); + + // Context-independent validity checks bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; - bool AcceptBlock(); -private: - bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew); + // Store block on disk + bool AcceptBlock(); }; +class CBlockFileInfo +{ +public: + unsigned int nBlocks; // number of blocks stored in file + unsigned int nSize; // number of used bytes of block file + unsigned int nUndoSize; // number of used bytes in the undo file + unsigned int nHeightFirst; // lowest height of block in file + unsigned int nHeightLast; // highest height of block in file + uint64 nTimeFirst; // earliest time of block in file + uint64 nTimeLast; // latest time of block in file + + IMPLEMENT_SERIALIZE( + READWRITE(VARINT(nBlocks)); + READWRITE(VARINT(nSize)); + READWRITE(VARINT(nUndoSize)); + READWRITE(VARINT(nHeightFirst)); + READWRITE(VARINT(nHeightLast)); + READWRITE(VARINT(nTimeFirst)); + READWRITE(VARINT(nTimeLast)); + ) + + void SetNull() { + nBlocks = 0; + nSize = 0; + nUndoSize = 0; + nHeightFirst = 0; + nHeightLast = 0; + nTimeFirst = 0; + nTimeLast = 0; + } + + CBlockFileInfo() { + SetNull(); + } + + std::string ToString() const { + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u..%u, time=%s..%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str()); + } + + // update statistics (does not update nSize) + void AddBlock(unsigned int nHeightIn, uint64 nTimeIn) { + if (nBlocks==0 || nHeightFirst > nHeightIn) + nHeightFirst = nHeightIn; + if (nBlocks==0 || nTimeFirst > nTimeIn) + nTimeFirst = nTimeIn; + nBlocks++; + if (nHeightIn > nHeightFirst) + nHeightLast = nHeightIn; + if (nTimeIn > nTimeLast) + nTimeLast = nTimeIn; + } +}; + +extern CCriticalSection cs_LastBlockFile; +extern CBlockFileInfo infoLastBlockFile; +extern int nLastBlockFile; + +enum BlockStatus { + BLOCK_VALID_UNKNOWN = 0, + BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future + BLOCK_VALID_TREE = 2, // parent found, difficulty matches, timestamp >= median previous, checkpoint + BLOCK_VALID_TRANSACTIONS = 3, // only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, sigops, size, merkle root + BLOCK_VALID_CHAIN = 4, // outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30 + BLOCK_VALID_SCRIPTS = 5, // scripts/signatures ok + BLOCK_VALID_MASK = 7, + + BLOCK_HAVE_DATA = 8, // full block available in blk*.dat + BLOCK_HAVE_UNDO = 16, // undo data available in rev*.dat + BLOCK_HAVE_MASK = 24, + + BLOCK_FAILED_VALID = 32, // stage after last reached validness failed + BLOCK_FAILED_CHILD = 64, // descends from failed block + BLOCK_FAILED_MASK = 96 +}; /** The block chain is a tree shaped structure starting with the * genesis block at the root, with each block potentially having multiple @@ -998,14 +1340,40 @@ private: class CBlockIndex { public: + // pointer to the hash of the block, if any. memory is owned by this CBlockIndex const uint256* phashBlock; + + // pointer to the index of the predecessor of this block CBlockIndex* pprev; + + // (memory only) pointer to the index of the *active* successor of this block CBlockIndex* pnext; - unsigned int nFile; - unsigned int nBlockPos; + + // height of the entry in the chain. The genesis block has height 0 int nHeight; + + // Which # file this block is stored in (blk?????.dat) + int nFile; + + // Byte offset within blk?????.dat where this block's data is stored + unsigned int nDataPos; + + // Byte offset within rev?????.dat where this block's undo data is stored + unsigned int nUndoPos; + + // (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block CBigNum bnChainWork; + // Number of transactions in this block. + // Note: in a potential headers-first mode, this number cannot be relied upon + unsigned int nTx; + + // (memory only) Number of transactions in the chain up to and including this block + unsigned int nChainTx; // change to 64-bit type when necessary; won't happen before 2030 + + // Verification status of this block. See enum BlockStatus + unsigned int nStatus; + // block header int nVersion; uint256 hashMerkleRoot; @@ -1019,10 +1387,14 @@ public: phashBlock = NULL; pprev = NULL; pnext = NULL; - nFile = 0; - nBlockPos = 0; nHeight = 0; + nFile = 0; + nDataPos = 0; + nUndoPos = 0; bnChainWork = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; nVersion = 0; hashMerkleRoot = 0; @@ -1031,15 +1403,19 @@ public: nNonce = 0; } - CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block) + CBlockIndex(CBlock& block) { phashBlock = NULL; pprev = NULL; pnext = NULL; - nFile = nFileIn; - nBlockPos = nBlockPosIn; nHeight = 0; + nFile = 0; + nDataPos = 0; + nUndoPos = 0; bnChainWork = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; nVersion = block.nVersion; hashMerkleRoot = block.hashMerkleRoot; @@ -1048,6 +1424,26 @@ public: nNonce = block.nNonce; } + CDiskBlockPos GetBlockPos() const { + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_DATA) { + ret.nFile = nFile; + ret.nPos = nDataPos; + } else + ret.SetNull(); + return ret; + } + + CDiskBlockPos GetUndoPos() const { + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_UNDO) { + ret.nFile = nFile; + ret.nPos = nUndoPos; + } else + ret.SetNull(); + return ret; + } + CBlock GetBlockHeader() const { CBlock block; @@ -1125,11 +1521,10 @@ public: static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck); - std::string ToString() const { - return strprintf("CBlockIndex(pprev=%p, pnext=%p, nFile=%u, nBlockPos=%-6u nHeight=%d, merkle=%s, hashBlock=%s)", - pprev, pnext, nFile, nBlockPos, nHeight, + return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, pnext, nHeight, hashMerkleRoot.ToString().substr(0,10).c_str(), GetBlockHash().ToString().substr(0,20).c_str()); } @@ -1140,6 +1535,19 @@ public: } }; +struct CBlockIndexWorkComparator +{ + bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + if (pa->bnChainWork > pb->bnChainWork) return false; + if (pa->bnChainWork < pb->bnChainWork) return true; + + if (pa->GetBlockHash() < pb->GetBlockHash()) return false; + if (pa->GetBlockHash() > pb->GetBlockHash()) return true; + + return false; // identical blocks + } +}; + /** Used to marshal pointers into hashes for db storage. */ @@ -1147,29 +1555,29 @@ class CDiskBlockIndex : public CBlockIndex { public: uint256 hashPrev; - uint256 hashNext; - CDiskBlockIndex() - { + CDiskBlockIndex() { hashPrev = 0; - hashNext = 0; } - explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) - { + explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) { hashPrev = (pprev ? pprev->GetBlockHash() : 0); - hashNext = (pnext ? pnext->GetBlockHash() : 0); } IMPLEMENT_SERIALIZE ( if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - - READWRITE(hashNext); - READWRITE(nFile); - READWRITE(nBlockPos); - READWRITE(nHeight); + READWRITE(VARINT(nVersion)); + + READWRITE(VARINT(nHeight)); + READWRITE(VARINT(nStatus)); + READWRITE(VARINT(nTx)); + if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) + READWRITE(VARINT(nFile)); + if (nStatus & BLOCK_HAVE_DATA) + READWRITE(VARINT(nDataPos)); + if (nStatus & BLOCK_HAVE_UNDO) + READWRITE(VARINT(nUndoPos)); // block header READWRITE(this->nVersion); @@ -1197,10 +1605,9 @@ public: { std::string str = "CDiskBlockIndex("; str += CBlockIndex::ToString(); - str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", + str += strprintf("\n hashBlock=%s, hashPrev=%s)", GetBlockHash().ToString().c_str(), - hashPrev.ToString().substr(0,20).c_str(), - hashNext.ToString().substr(0,20).c_str()); + hashPrev.ToString().substr(0,20).c_str()); return str; } @@ -1358,12 +1765,12 @@ public: std::map<uint256, CTransaction> mapTx; std::map<COutPoint, CInPoint> mapNextTx; - bool accept(CTxDB& txdb, CTransaction &tx, - bool fCheckInputs, bool* pfMissingInputs); + bool accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs); bool addUnchecked(const uint256& hash, CTransaction &tx); bool remove(CTransaction &tx); void clear(); void queryHashes(std::vector<uint256>& vtxid); + void pruneSpent(const uint256& hash, CCoins &coins); unsigned long size() { @@ -1384,4 +1791,97 @@ public: extern CTxMemPool mempool; +struct CCoinsStats +{ + int nHeight; + uint64 nTransactions; + uint64 nTransactionOutputs; + uint64 nSerializedSize; + + CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0) {} +}; + +/** Abstract view on the open txout dataset. */ +class CCoinsView +{ +public: + // Retrieve the CCoins (unspent transaction outputs) for a given txid + virtual bool GetCoins(uint256 txid, CCoins &coins); + + // Modify the CCoins for a given txid + virtual bool SetCoins(uint256 txid, const CCoins &coins); + + // Just check whether we have data for a given txid. + // This may (but cannot always) return true for fully spent transactions + virtual bool HaveCoins(uint256 txid); + + // Retrieve the block index whose state this CCoinsView currently represents + virtual CBlockIndex *GetBestBlock(); + + // Modify the currently active block index + virtual bool SetBestBlock(CBlockIndex *pindex); + virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + virtual bool GetStats(CCoinsStats &stats); +}; + +/** CCoinsView backed by another CCoinsView */ +class CCoinsViewBacked : public CCoinsView +{ +protected: + CCoinsView *base; + +public: + CCoinsViewBacked(CCoinsView &viewIn); + bool GetCoins(uint256 txid, CCoins &coins); + bool SetCoins(uint256 txid, const CCoins &coins); + bool HaveCoins(uint256 txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + void SetBackend(CCoinsView &viewIn); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + bool GetStats(CCoinsStats &stats); +}; + +/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ +class CCoinsViewCache : public CCoinsViewBacked +{ +protected: + CBlockIndex *pindexTip; + std::map<uint256,CCoins> cacheCoins; + +public: + CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); + bool GetCoins(uint256 txid, CCoins &coins); + bool SetCoins(uint256 txid, const CCoins &coins); + bool HaveCoins(uint256 txid); + CCoins &GetCoins(uint256 txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + bool Flush(); + unsigned int GetCacheSize(); + +private: + std::map<uint256,CCoins>::iterator FetchCoins(uint256 txid); +}; + +/** CCoinsView that brings transactions from a memorypool into view. + It does not check for spendings by memory pool transactions. */ +class CCoinsViewMemPool : public CCoinsViewBacked +{ +protected: + CTxMemPool &mempool; + +public: + CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); + bool GetCoins(uint256 txid, CCoins &coins); + bool HaveCoins(uint256 txid); +}; + +/** Global variable that points to the active CCoinsView (protected by cs_main) */ +extern CCoinsViewCache *pcoinsTip; + +/** Global variable that points to the active block tree (protected by cs_main) */ +extern CBlockTreeDB *pblocktree; + #endif diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 0970469a98..f4adbb2bff 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -82,10 +82,19 @@ OBJS= \ obj/util.o \ obj/wallet.o \ obj/walletdb.o \ - obj/noui.o + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o all: bitcoind.exe +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += -I"$(CURDIR)/leveldb/include" +DEFS += -I"$(CURDIR)/leveldb/helpers" +leveldb/libleveldb.a: + @echo "Building LevelDB ..."; cd leveldb; TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$(INCLUDEPATHS)" LDFLAGS="-L$(LIBPATHS)" make libleveldb.a libmemenv.a; cd .. +obj/leveldb.o: leveldb/libleveldb.a + obj/build.h: FORCE /bin/sh ../share/genbuild.sh obj/build.h version.cpp: obj/build.h diff --git a/src/makefile.mingw b/src/makefile.mingw index 54054c25f9..89a4f372c5 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -78,7 +78,9 @@ OBJS= \ obj/util.o \ obj/wallet.o \ obj/walletdb.o \ - obj/noui.o + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o all: bitcoind.exe @@ -86,6 +88,16 @@ all: bitcoind.exe test check: test_bitcoin.exe FORCE test_bitcoin.exe +# +# LevelDB support +# +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +leveldb/libleveldb.a: + cd leveldb; make libleveldb.a libmemenv.a; cd .. +obj/leveldb.o: leveldb/libleveldb.lib + obj/%.o: %.cpp $(HEADERS) g++ -c $(CFLAGS) -o $@ $< diff --git a/src/makefile.osx b/src/makefile.osx index 359739bd5e..f3e17d0d13 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -96,7 +96,9 @@ OBJS= \ obj/util.o \ obj/wallet.o \ obj/walletdb.o \ - obj/noui.o + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o ifndef USE_UPNP override USE_UPNP = - @@ -119,6 +121,16 @@ all: bitcoind test check: test_bitcoin FORCE ./test_bitcoin +# +# LevelDB support +# +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +leveldb/libleveldb.a: + @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd .. +obj/leveldb.o: leveldb/libleveldb.a + # auto-generated dependencies: -include obj/*.P -include obj-test/*.P diff --git a/src/makefile.unix b/src/makefile.unix index a41b57b4fc..df05f7990a 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -127,7 +127,9 @@ OBJS= \ obj/util.o \ obj/wallet.o \ obj/walletdb.o \ - obj/noui.o + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o all: bitcoind @@ -135,6 +137,16 @@ all: bitcoind test check: test_bitcoin FORCE ./test_bitcoin +# +# LevelDB support +# +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +leveldb/libleveldb.a: + @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..; +obj/leveldb.o: leveldb/libleveldb.a + # auto-generated dependencies: -include obj/*.P -include obj-test/*.P diff --git a/src/net.cpp b/src/net.cpp index 7c327f5d35..2598f0214e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -7,7 +7,6 @@ #include "db.h" #include "net.h" #include "init.h" -#include "strlcpy.h" #include "addrman.h" #include "ui_interface.h" @@ -1185,7 +1184,7 @@ void ThreadDNSAddressSeed2(void* parg) printf("Loading addresses from DNS seeds (could take a while)\n"); for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { - if (GetNameProxy()) { + if (HaveNameProxy()) { AddOneShot(strDNSSeed[seed_idx][1]); } else { vector<CNetAddr> vaddr; @@ -1529,7 +1528,7 @@ void ThreadOpenAddedConnections2(void* parg) if (mapArgs.count("-addnode") == 0) return; - if (GetNameProxy()) { + if (HaveNameProxy()) { while(!fShutdown) { BOOST_FOREACH(string& strAddNode, mapMultiArgs["-addnode"]) { CAddress addr; @@ -106,6 +106,7 @@ enum threadId THREAD_ADDEDCONNECTIONS, THREAD_DUMPADDRESS, THREAD_RPCHANDLER, + THREAD_IMPORT, THREAD_MAX }; diff --git a/src/netbase.cpp b/src/netbase.cpp index daa8a8d07e..7b28e7f1bc 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -5,20 +5,21 @@ #include "netbase.h" #include "util.h" +#include "sync.h" #ifndef WIN32 #include <sys/fcntl.h> #endif -#include "strlcpy.h" #include <boost/algorithm/string/case_conv.hpp> // for to_lower() +#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith() using namespace std; // Settings -typedef std::pair<CService, int> proxyType; static proxyType proxyInfo[NET_MAX]; static proxyType nameproxyInfo; +static CCriticalSection cs_proxyInfos; int nConnectTimeout = 5000; bool fNameLookup = false; @@ -29,7 +30,6 @@ enum Network ParseNetwork(std::string net) { if (net == "ipv4") return NET_IPV4; if (net == "ipv6") return NET_IPV6; if (net == "tor") return NET_TOR; - if (net == "i2p") return NET_I2P; return NET_UNROUTABLE; } @@ -118,18 +118,16 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup) { - if (pszName[0] == 0) + std::string str(pszName); + std::string strHost = str; + if (str.empty()) return false; - char psz[256]; - char *pszHost = psz; - strlcpy(psz, pszName, sizeof(psz)); - if (psz[0] == '[' && psz[strlen(psz)-1] == ']') + if (boost::algorithm::starts_with(str, "[") && boost::algorithm::ends_with(str, "]")) { - pszHost = psz+1; - psz[strlen(psz)-1] = 0; + strHost = str.substr(1, str.size() - 2); } - return LookupIntern(pszHost, vIP, nMaxSolutions, fAllowLookup); + return LookupIntern(strHost.c_str(), vIP, nMaxSolutions, fAllowLookup); } bool LookupHostNumeric(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions) @@ -432,15 +430,17 @@ bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion) { return false; if (nSocksVersion != 0 && !addrProxy.IsValid()) return false; + LOCK(cs_proxyInfos); proxyInfo[net] = std::make_pair(addrProxy, nSocksVersion); return true; } -bool GetProxy(enum Network net, CService &addrProxy) { +bool GetProxy(enum Network net, proxyType &proxyInfoOut) { assert(net >= 0 && net < NET_MAX); + LOCK(cs_proxyInfos); if (!proxyInfo[net].second) return false; - addrProxy = proxyInfo[net].first; + proxyInfoOut = proxyInfo[net]; return true; } @@ -449,16 +449,27 @@ bool SetNameProxy(CService addrProxy, int nSocksVersion) { return false; if (nSocksVersion != 0 && !addrProxy.IsValid()) return false; + LOCK(cs_proxyInfos); nameproxyInfo = std::make_pair(addrProxy, nSocksVersion); return true; } -bool GetNameProxy() { +bool GetNameProxy(proxyType &nameproxyInfoOut) { + LOCK(cs_proxyInfos); + if (!nameproxyInfo.second) + return false; + nameproxyInfoOut = nameproxyInfo; + return true; +} + +bool HaveNameProxy() { + LOCK(cs_proxyInfos); return nameproxyInfo.second != 0; } bool IsProxy(const CNetAddr &addr) { - for (int i=0; i<NET_MAX; i++) { + LOCK(cs_proxyInfos); + for (int i = 0; i < NET_MAX; i++) { if (proxyInfo[i].second && (addr == (CNetAddr)proxyInfo[i].first)) return true; } @@ -467,10 +478,10 @@ bool IsProxy(const CNetAddr &addr) { bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout) { - const proxyType &proxy = proxyInfo[addrDest.GetNetwork()]; + proxyType proxy; // no proxy needed - if (!proxy.second) + if (!GetProxy(addrDest.GetNetwork(), proxy)) return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); SOCKET hSocket = INVALID_SOCKET; @@ -504,19 +515,22 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest SplitHostPort(string(pszDest), port, strDest); SOCKET hSocket = INVALID_SOCKET; - CService addrResolved(CNetAddr(strDest, fNameLookup && !nameproxyInfo.second), port); + + proxyType nameproxy; + GetNameProxy(nameproxy); + + CService addrResolved(CNetAddr(strDest, fNameLookup && !nameproxy.second), port); if (addrResolved.IsValid()) { addr = addrResolved; return ConnectSocket(addr, hSocketRet, nTimeout); } addr = CService("0.0.0.0:0"); - if (!nameproxyInfo.second) + if (!nameproxy.second) return false; - if (!ConnectSocketDirectly(nameproxyInfo.first, hSocket, nTimeout)) + if (!ConnectSocketDirectly(nameproxy.first, hSocket, nTimeout)) return false; - switch(nameproxyInfo.second) - { + switch(nameproxy.second) { default: case 4: return false; case 5: @@ -540,7 +554,6 @@ void CNetAddr::SetIP(const CNetAddr& ipIn) } static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; -static const unsigned char pchGarliCat[] = {0xFD,0x60,0xDB,0x4D,0xDD,0xB5}; bool CNetAddr::SetSpecial(const std::string &strName) { @@ -553,15 +566,6 @@ bool CNetAddr::SetSpecial(const std::string &strName) ip[i + sizeof(pchOnionCat)] = vchAddr[i]; return true; } - if (strName.size()>11 && strName.substr(strName.size() - 11, 11) == ".oc.b32.i2p") { - std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 11).c_str()); - if (vchAddr.size() != 16-sizeof(pchGarliCat)) - return false; - memcpy(ip, pchOnionCat, sizeof(pchGarliCat)); - for (unsigned int i=0; i<16-sizeof(pchGarliCat); i++) - ip[i + sizeof(pchGarliCat)] = vchAddr[i]; - return true; - } return false; } @@ -611,7 +615,7 @@ bool CNetAddr::IsIPv4() const bool CNetAddr::IsIPv6() const { - return (!IsIPv4() && !IsTor() && !IsI2P()); + return (!IsIPv4() && !IsTor()); } bool CNetAddr::IsRFC1918() const @@ -675,11 +679,6 @@ bool CNetAddr::IsTor() const return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0); } -bool CNetAddr::IsI2P() const -{ - return (memcmp(ip, pchGarliCat, sizeof(pchGarliCat)) == 0); -} - bool CNetAddr::IsLocal() const { // IPv4 loopback @@ -738,7 +737,7 @@ bool CNetAddr::IsValid() const bool CNetAddr::IsRoutable() const { - return IsValid() && !(IsRFC1918() || IsRFC3927() || IsRFC4862() || (IsRFC4193() && !IsTor() && !IsI2P()) || IsRFC4843() || IsLocal()); + return IsValid() && !(IsRFC1918() || IsRFC3927() || IsRFC4862() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal()); } enum Network CNetAddr::GetNetwork() const @@ -752,9 +751,6 @@ enum Network CNetAddr::GetNetwork() const if (IsTor()) return NET_TOR; - if (IsI2P()) - return NET_I2P; - return NET_IPV6; } @@ -762,8 +758,6 @@ std::string CNetAddr::ToStringIP() const { if (IsTor()) return EncodeBase32(&ip[6], 10) + ".onion"; - if (IsI2P()) - return EncodeBase32(&ip[6], 10) + ".oc.b32.i2p"; CService serv(*this, 0); #ifdef USE_IPV6 struct sockaddr_storage sockaddr; @@ -871,12 +865,6 @@ std::vector<unsigned char> CNetAddr::GetGroup() const nStartByte = 6; nBits = 4; } - else if (IsI2P()) - { - nClass = NET_I2P; - nStartByte = 6; - nBits = 4; - } // for he.net, use /36 groups else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && GetByte(13) == 0x04 && GetByte(12) == 0x70) nBits = 36; @@ -962,11 +950,6 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well case NET_TOR: return REACH_PRIVATE; } - case NET_I2P: - switch(ourNet) { - default: return REACH_DEFAULT; - case NET_I2P: return REACH_PRIVATE; - } case NET_TEREDO: switch(ourNet) { default: return REACH_DEFAULT; @@ -982,8 +965,7 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const case NET_TEREDO: return REACH_TEREDO; case NET_IPV6: return REACH_IPV6_WEAK; case NET_IPV4: return REACH_IPV4; - case NET_I2P: return REACH_PRIVATE; // assume connections from unroutable addresses are - case NET_TOR: return REACH_PRIVATE; // either from Tor/I2P, or don't care about our address + case NET_TOR: return REACH_PRIVATE; // either from Tor, or don't care about our address } } } @@ -1140,7 +1122,7 @@ std::string CService::ToStringPort() const std::string CService::ToStringIPPort() const { - if (IsIPv4() || IsTor() || IsI2P()) { + if (IsIPv4() || IsTor()) { return ToStringIP() + ":" + ToStringPort(); } else { return "[" + ToStringIP() + "]:" + ToStringPort(); diff --git a/src/netbase.h b/src/netbase.h index 560e2182df..e4ec4ef597 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -23,7 +23,6 @@ enum Network NET_IPV4, NET_IPV6, NET_TOR, - NET_I2P, NET_MAX, }; @@ -44,9 +43,9 @@ class CNetAddr explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false); void Init(); void SetIP(const CNetAddr& ip); - bool SetSpecial(const std::string &strName); // for Tor and I2P addresses + bool SetSpecial(const std::string &strName); // for Tor addresses bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) - bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor/I2P) + bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor) bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16) @@ -58,7 +57,6 @@ class CNetAddr bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96) bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) bool IsTor() const; - bool IsI2P() const; bool IsLocal() const; bool IsRoutable() const; bool IsValid() const; @@ -133,13 +131,15 @@ class CService : public CNetAddr ) }; +typedef std::pair<CService, int> proxyType; + enum Network ParseNetwork(std::string net); void SplitHostPort(std::string in, int &portOut, std::string &hostOut); bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5); -bool GetProxy(enum Network net, CService &addrProxy); +bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); bool SetNameProxy(CService addrProxy, int nSocksVersion = 5); -bool GetNameProxy(); +bool HaveNameProxy(); bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true); bool LookupHostNumeric(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0); bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 19a6a65a1b..0d269ea210 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -473,7 +473,7 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) { // don't show / hide progress bar and its label if we have no connection to the network - if (!clientModel || clientModel->getNumConnections() == 0) + if (!clientModel || (clientModel->getNumConnections() == 0 && !clientModel->isImporting())) { progressBarLabel->setVisible(false); progressBar->setVisible(false); @@ -491,7 +491,7 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) if (strStatusBarWarnings.isEmpty()) { - progressBarLabel->setText(tr("Synchronizing with network...")); + progressBarLabel->setText(tr(clientModel->isImporting() ? "Importing blocks..." : "Synchronizing with network...")); progressBarLabel->setVisible(true); progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); progressBar->setMaximum(nTotalBlocks); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 53e729727e..b92a26c4a4 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -19,7 +19,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:" "@STRENGTH)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"An error occurred while setting up the RPC port %i for listening on IPv6, " +"An error occurred while setting up the RPC port %u for listening on IPv6, " "falling back to IPv4: %s"), QT_TRANSLATE_NOOP("bitcoin-core", "" "An error occurred while setting up the RPC port %u for listening on IPv4: %s"), diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index b820d16abf..990b364a94 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -103,6 +103,11 @@ bool ClientModel::inInitialBlockDownload() const return IsInitialBlockDownload(); } +bool ClientModel::isImporting() const +{ + return fImporting; +} + int ClientModel::getNumBlocksOfPeers() const { return GetNumBlocksOfPeers(); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 70d816ba9d..926390a07a 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -33,6 +33,8 @@ public: bool isTestNet() const; //! Return true if core is doing initial block download bool inInitialBlockDownload() const; + //! Return true if core is importing blocks + bool isImporting() const; //! Return conservative estimate of total number of blocks, or 0 if unknown int getNumBlocksOfPeers() const; //! Return warnings to be displayed in status bar diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index d2d7716dbd..ff70ca24af 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -439,7 +439,7 @@ void HelpMessageBox::printToConsole() { // On other operating systems, the expected action is to print the message to the console. QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions; - fprintf(stderr, "%s", strUsage.toStdString().c_str()); + fprintf(stdout, "%s", strUsage.toStdString().c_str()); } void HelpMessageBox::showOrPrint() diff --git a/src/qt/locale/bitcoin_bg.ts b/src/qt/locale/bitcoin_bg.ts index 989db004d7..4cefec9848 100644 --- a/src/qt/locale/bitcoin_bg.ts +++ b/src/qt/locale/bitcoin_bg.ts @@ -82,11 +82,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Подпиши съобщение</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -96,12 +101,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Провери съобщение</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Изтрива избрания адрес. Не могат да се изтриват входящи адреси.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Изтрий</translation> </message> @@ -232,24 +232,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Портфейлът е криптиран</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Подписване на &съобщение...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Синхронизиране с мрежата...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Баланс</translation> </message> @@ -313,7 +318,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Обобщена информация за портфейла</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Транзакции</translation> </message> @@ -333,7 +338,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Редактиране на адреси</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Получаване</translation> </message> @@ -343,12 +348,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Списък на адресите за получаване</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Изпращане</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>Из&ход</translation> </message> @@ -378,7 +383,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Опции...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Криптиране на портфейла...</translation> </message> @@ -403,42 +408,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>%1 (of %2) блока (%3%%).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Експорт...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Изпращане към Биткоин адрес</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Подпишете съобщение като доказателство, че притежавате определен адрес</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation type="unfinished"/> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Запишете данните от текущия раздел във файл</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Криптира или декриптира портфейла</translation> </message> @@ -453,7 +443,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Променя паролата за портфейла</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -463,12 +453,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Проверка на съобщение...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Биткоин</translation> </message> @@ -478,7 +468,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Портфейл</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&За Биткоин</translation> </message> @@ -488,12 +478,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Файл</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Настройки</translation> </message> @@ -508,7 +498,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Раздели</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Функции</translation> </message> @@ -525,7 +515,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n връзка към Биткоин мрежата</numerusform><numerusform>%n връзки към Биткоин мрежата</numerusform></translation> </message> @@ -599,7 +589,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1106,7 +1096,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>N/A</translation> </message> @@ -1257,8 +1247,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Добави получател</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1287,8 +1277,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Изпращане</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1643,7 +1633,7 @@ Address: %4 <translation>Издадени</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>От</translation> @@ -1754,12 +1744,12 @@ Address: %4 <translation>false</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, все още не е изпратено</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>неизвестен</translation> </message> @@ -2070,7 +2060,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Биткоин версия</translation> </message> @@ -2085,12 +2075,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation type="unfinished"/> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation type="unfinished"/> </message> @@ -2165,22 +2155,47 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation type="unfinished"/> </message> @@ -2190,7 +2205,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2215,12 +2230,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2245,17 +2255,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Невалиден -tor адрес: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2360,27 +2365,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation type="unfinished"/> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation type="unfinished"/> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2405,12 +2405,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation type="unfinished"/> </message> @@ -2425,22 +2425,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation type="unfinished"/> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation type="unfinished"/> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Достъпът до %s е невъзможен. Биткоин е вече стартитан?</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Биткоин</translation> </message> @@ -2455,12 +2455,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Зареждане на адресите...</translation> </message> @@ -2490,12 +2490,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Невалиден -proxy address: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2515,12 +2515,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2530,12 +2530,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Размерът на транзакцията ще надвиши максималният размер за безплатна транзакция. Можете да я изпратите срещу такса от %1, която ще бъде получена от участниците в мрежата, обработващи транзакции. Желаете ли да платите таксата?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Грешка: създаването на транзакция беше неуспешно </translation> </message> @@ -2545,12 +2545,12 @@ Address: %4 <translation>Изпращане...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Грешка: транзакцията беше отхвърлена. Това е възможно ако част от парите в портфейла са вече похарчени, например при паралелно използване на копие на wallet.dat</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation type="unfinished"/> </message> @@ -2560,12 +2560,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Зареждане на блок индекса...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation type="unfinished"/> </message> @@ -2575,7 +2575,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2620,7 +2620,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2632,12 +2632,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Грешка</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_ca_ES.ts b/src/qt/locale/bitcoin_ca_ES.ts index 404b9d1adc..4ccfa55882 100644 --- a/src/qt/locale/bitcoin_ca_ES.ts +++ b/src/qt/locale/bitcoin_ca_ES.ts @@ -77,26 +77,26 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> <translation type="unfinished"/> </message> <message> - <location line="+3"/> - <source>&Verify Message</source> + <location line="-14"/> + <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> + <location line="+3"/> + <source>&Verify Message</source> <translation type="unfinished"/> </message> <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Borrar</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation type="unfinished"/> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation type="unfinished"/> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Sincronització amb la xarxa ...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation type="unfinished"/> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Mostra panorama general de la cartera</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation type="unfinished"/> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Edita la llista d'adreces emmagatzemada i etiquetes</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Rebre monedes</translation> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation type="unfinished"/> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation type="unfinished"/> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Opcions ...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation type="unfinished"/> </message> @@ -398,42 +403,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation type="unfinished"/> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation type="unfinished"/> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation type="unfinished"/> </message> @@ -448,7 +438,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -458,12 +448,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -473,7 +463,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation type="unfinished"/> </message> @@ -483,12 +473,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation type="unfinished"/> </message> @@ -503,7 +493,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Accions de la barra d'eines</translation> </message> @@ -520,7 +510,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation> </message> @@ -594,7 +584,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1101,7 +1091,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1252,7 +1242,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation type="unfinished"/> </message> <message> @@ -1282,7 +1272,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation type="unfinished"/> </message> <message> @@ -1638,7 +1628,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation type="unfinished"/> @@ -1749,12 +1739,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation type="unfinished"/> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation type="unfinished"/> </message> @@ -2065,7 +2055,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation type="unfinished"/> </message> @@ -2080,12 +2070,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation type="unfinished"/> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation type="unfinished"/> </message> @@ -2160,22 +2150,47 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation type="unfinished"/> </message> @@ -2185,7 +2200,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2210,12 +2225,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2240,17 +2250,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2355,27 +2360,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation type="unfinished"/> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation type="unfinished"/> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2400,12 +2400,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation type="unfinished"/> </message> @@ -2420,22 +2420,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation type="unfinished"/> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation type="unfinished"/> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation type="unfinished"/> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -2450,12 +2450,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation type="unfinished"/> </message> @@ -2485,12 +2485,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2510,12 +2510,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2525,12 +2525,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation type="unfinished"/> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation type="unfinished"/> </message> @@ -2540,12 +2540,12 @@ Address: %4 <translation>L'enviament de ...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation type="unfinished"/> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation type="unfinished"/> </message> @@ -2555,12 +2555,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation type="unfinished"/> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation type="unfinished"/> </message> @@ -2570,7 +2570,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2615,7 +2615,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2627,12 +2627,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation type="unfinished"/> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts index c48b08c6bf..02f34e5ec9 100644 --- a/src/qt/locale/bitcoin_cs.ts +++ b/src/qt/locale/bitcoin_cs.ts @@ -82,11 +82,16 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation>Po&depiš zprávu</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>Smaž zvolenou adresu ze seznamu</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Ověř zprávu, aby ses ujistil, že byla podepsána danou Bitcoinovou adresou</translation> </message> @@ -96,12 +101,7 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>&Ověř zprávu</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Smaž aktuálně vybranou adresu ze seznamu. Smazány mohou být pouze adresy příjemců.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>S&maž</translation> </message> @@ -232,24 +232,29 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Jsi si jistý, že chceš peněženku zašifrovat?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>DŮLEŽITÉ: Všechny předchozí zálohy peněženky by měly být nahrazeny nově vygenerovanou, zašifrovanou peněženkou. Z bezpečnostních důvodů budou předchozí zálohy nešifrované peněženky nepoužitelné, jakmile zaneš používat novou zašifrovanou peněženku.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Upozornění: Caps Lock je zapnutý!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Peněženka je zašifrována</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin se teď ukončí, aby dokončil zašifrování. Pamatuj však, že pouhé zašifrování peněženky úplně nezabraňuje krádeži tvých bitcoinů malwarem, kterým se může počítač nakazit.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Po&depiš zprávu...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synchronizuji se sítí...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Přehled</translation> </message> @@ -313,7 +318,7 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Zobraz celkový přehled peněženky</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transakce</translation> </message> @@ -333,7 +338,7 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Uprav seznam uložených adres a jejich označení</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>Pří&jem mincí</translation> </message> @@ -343,12 +348,12 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Zobraz seznam adres pro příjem plateb</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>P&oslání mincí</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Konec</translation> </message> @@ -378,7 +383,7 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>&Možnosti...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>Zaši&fruj peněženku...</translation> </message> @@ -403,42 +408,27 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Staženo %1 z %2 bloků transakční historie (%3 % hotovo).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Export...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Pošli mince na Bitcoinovou adresu</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Podepiš zprávu, čímž prokážeš, že jsi vlastníkem Bitcoinové adresy</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Ověř zprávu, aby ses ujistil, že byla podepsána danou Bitcoinovou adresou</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>&Podpisy</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Uprav nastavení Bitcoinu</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Exportovat data z tohoto panelu do souboru</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Zašifruj nebo dešifruj peněženku</translation> </message> @@ -453,7 +443,7 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Změň heslo k šifrování peněženky</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Ladicí okno</translation> </message> @@ -463,12 +453,12 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Otevři ladicí a diagnostickou konzoli</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Ověř zprávu...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,22 +468,22 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Peněženka</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>O &Bitcoinu</translation> </message> <message> <location line="+9"/> <source>&Show / Hide</source> - <translation type="unfinished"/> + <translation>&Zobraz/Skryj</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Soubor</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Nastavení</translation> </message> @@ -508,7 +498,7 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Panel s listy</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Panel akcí</translation> </message> @@ -525,7 +515,7 @@ Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v Open <translation>Bitcoin klient</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktivní spojení do Bitcoinové sítě</numerusform><numerusform>%n aktivní spojení do Bitcoinové sítě</numerusform><numerusform>%n aktivních spojení do Bitcoinové sítě</numerusform></translation> </message> @@ -603,7 +593,7 @@ Adresa: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>Zpracování URI</translation> @@ -1110,7 +1100,7 @@ Adresa: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>N/A</translation> </message> @@ -1261,7 +1251,7 @@ Adresa: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation>Při&dej příjemce</translation> </message> <message> @@ -1291,7 +1281,7 @@ Adresa: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation>&Pošli</translation> </message> <message> @@ -1647,7 +1637,7 @@ Adresa: %4 <translation>Vygenerováno</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Od</translation> @@ -1758,12 +1748,12 @@ Adresa: %4 <translation>false</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, ještě nebylo rozesláno</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>neznámo</translation> </message> @@ -2074,7 +2064,7 @@ Adresa: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Verze Bitcoinu</translation> </message> @@ -2089,12 +2079,12 @@ Adresa: %4 <translation>Poslat příkaz pro -server nebo bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Výpis příkazů</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Získat nápovědu pro příkaz</translation> </message> @@ -2169,22 +2159,47 @@ Adresa: %4 <translation>Práh pro odpojování nesprávně se chovajících uzlů (výchozí: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Doba ve vteřinách, po kterou se nebudou moci nesprávně se chovající uzly znovu připojit (výchozí: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>Při nastavování naslouchacího RPC portu %i pro IPv6 nastala chyba, vracím se k IPv4: %s</translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>Při nastavování naslouchacího RPC portu %i pro IPv4 nastala chyba: %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Odpojit databázi bloků a adres. Prodlužuje čas potřebný k ukončení (výchozí: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>Čekat na JSON RPC spojení na <portu> (výchozí: 8332 nebo testnet: 18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Akceptovat příkazy z příkazové řádky a přes JSON-RPC</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>Importuji soubor s řetězcem bloků.</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>Importuji iniciální soubor s řetězcem bloků.</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Běžet na pozadí jako démon a akceptovat příkazy</translation> </message> @@ -2194,7 +2209,7 @@ Adresa: %4 <translation>Použít testovací síť (testnet)</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Přijímat spojení zvenčí (výchozí: 1, pokud není zadáno -proxy nebo -connect)</translation> </message> @@ -2219,12 +2234,7 @@ Adresa: %4 <translation>Upozornění: Zkontroluj, že máš v počítači správně nastavený datum a čas! Pokud jsou nastaveny špatně, Bitcoin nebude fungovat správně.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Při nastavování naslouchacího RPC portu %i nastala chyba: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Možnosti vytvoření bloku:</translation> </message> @@ -2249,17 +2259,12 @@ Adresa: %4 <translation>Hledat uzly přes DNS (výchozí: 1, pokud není zadáno -connect)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Neplatná -tor adresa: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Maximální velikost přijímacího bufferu pro každé spojení, <n>*1000 bajtů (výchozí: 5000)</translation> </message> @@ -2326,7 +2331,7 @@ Adresa: %4 <message> <location line="+2"/> <source>Specify connection timeout in milliseconds (default: 5000)</source> - <translation type="unfinished"/> + <translation>Zadej časový limit spojení v milisekundách (výchozí: 5000)</translation> </message> <message> <location line="+13"/> @@ -2364,27 +2369,22 @@ Adresa: %4 <translation>Heslo pro JSON-RPC spojení</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Čekat na JSON-RPC spojení na <portu> (výchozí: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Povolit JSON-RPC spojení ze specifikované IP adresy</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Posílat příkazy uzlu běžícím na <ip> (výchozí: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Spustit příkaz, když se změní nejlepší blok (%s se v příkazu nahradí hashem bloku)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Převést peněženku na nejnovější formát</translation> </message> @@ -2409,12 +2409,12 @@ Adresa: %4 <translation>Jak moc důkladná má verifikace bloků být (0-6, výchozí: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Importuje bloky z externího souboru blk000?.dat</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Použít OpenSSL (https) pro JSON-RPC spojení</translation> </message> @@ -2429,22 +2429,22 @@ Adresa: %4 <translation>Soubor se serverovým soukromým klíčem (výchozí: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Akceptovatelné šifry (výchozí: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Tato nápověda</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Nedaří se mi získat zámek na datový adresář %s. Bitcoin pravděpodobně už jednou běží.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2459,12 +2459,12 @@ Adresa: %4 <translation>Připojovat se přes socks proxy</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Povolit DNS dotazy pro -addnode (přidání uzlu), -seednode a -connect (připojení)</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Načítám adresy...</translation> </message> @@ -2494,12 +2494,12 @@ Adresa: %4 <translation>Chyba při načítání wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Neplatná -proxy adresa: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>V -onlynet byla uvedena neznámá síť: '%s'</translation> </message> @@ -2519,12 +2519,12 @@ Adresa: %4 <translation>Nemohu přeložit -externalip adresu: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Neplatná částka pro -paytxfee=<částka>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Chyba: nemohu nastartovat uzel</translation> </message> @@ -2534,12 +2534,12 @@ Adresa: %4 <translation>Chyba: Peněženka je zamčená, nemohu vytvořit transakci </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Chyba: Tahle transakce vyžaduje transakční poplatek nejméně %s kvůli velikosti zasílané částky, komplexnosti nebo použití nedávno přijatých mincí </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Chyba: Vytvoření transakce selhalo </translation> </message> @@ -2549,12 +2549,12 @@ Adresa: %4 <translation>Posílám...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Chyba Transakce byla odmítnuta. Tohle může nastat, pokud nějaké mince z tvé peněženky už jednou byly utraceny, například pokud používáš kopii souboru wallet.dat a mince byly utraceny v druhé kopii, ale nebyly označeny jako utracené v této.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Neplatná částka</translation> </message> @@ -2564,12 +2564,12 @@ Adresa: %4 <translation>Nedostatek prostředků</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Načítám index bloků...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Přidat uzel, ke kterému se připojit a snažit se spojení udržet</translation> </message> @@ -2579,7 +2579,7 @@ Adresa: %4 <translation>Nedaří se mi připojit na %s na tomhle počítači. Bitcoin už pravděpodobně jednou běží.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Hledat uzly přes IRC (výchozí: 0)</translation> </message> @@ -2624,7 +2624,7 @@ Adresa: %4 <translation>K použití volby %s</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2643,12 +2643,12 @@ Pokud konfigurační soubor ještě neexistuje, vytvoř ho tak, aby ho mohl čí </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Chyba</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts index 3f5368f7be..9f327e71db 100644 --- a/src/qt/locale/bitcoin_da.ts +++ b/src/qt/locale/bitcoin_da.ts @@ -82,11 +82,16 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -96,12 +101,7 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Slet den valgte adresse fra listen. Kun adresser brugt til afsendelse kan slettes.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Slet</translation> </message> @@ -232,24 +232,29 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation>Er du sikker på at du ønsker at kryptere din tegnebog?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Tegnebog krypteret</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin will close now to finish the encryption process. Husk, at kryptere din tegnebog vil ikke fuldt ud beskytte dine bitcoins mod at blive stjålet af malware på din computer.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation type="unfinished"/> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synkroniserer med netværk ...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Oversigt</translation> </message> @@ -313,7 +318,7 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation>Vis generel oversigt over tegnebog</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transaktioner</translation> </message> @@ -333,7 +338,7 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation>Rediger listen over gemte adresser og etiketter</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Modtag coins</translation> </message> @@ -343,12 +348,12 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation>Vis listen over adresser for at modtage betalinger</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Send coins</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Luk</translation> </message> @@ -378,7 +383,7 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation>&Indstillinger ...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Kryptér tegnebog...</translation> </message> @@ -403,42 +408,27 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation>Downloadet %1 af %2 blokke af transaktionshistorie (%3% done).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Eksporter...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins til en bitcoinadresse</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Rediger konfigurationsindstillinger af bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Eksportér den aktuelle visning til en fil</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Kryptér eller dekryptér tegnebog</translation> </message> @@ -453,7 +443,7 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation>Skift kodeord anvendt til tegnebogskryptering</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -463,12 +453,12 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,7 +468,7 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation>tegnebog</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Om Bitcoin</translation> </message> @@ -488,12 +478,12 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Fil</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Indstillinger</translation> </message> @@ -508,7 +498,7 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation>Faneværktøjslinje</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Handlingsværktøjslinje</translation> </message> @@ -525,7 +515,7 @@ Produktet indeholder software som er udviklet af OpenSSL Project til brug i Open <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktiv(e) forbindelse(r) til Bitcoinnetværket</numerusform><numerusform>%n aktiv(e) forbindelse(r) til Bitcoinnetværket</numerusform></translation> </message> @@ -603,7 +593,7 @@ Adresse: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1111,7 +1101,7 @@ Adresse: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1262,8 +1252,8 @@ Adresse: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Tilføj modtager</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1292,8 +1282,8 @@ Adresse: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Afsend</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1648,7 +1638,7 @@ Adresse: %4 <translation>Genereret</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Fra</translation> @@ -1759,12 +1749,12 @@ Adresse: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, er ikke blevet transmitteret endnu</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>ukendt</translation> </message> @@ -2075,7 +2065,7 @@ Adresse: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoinversion</translation> </message> @@ -2091,13 +2081,13 @@ Adresse: %4 </translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Liste over kommandoer </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Få hjælp til en kommando </translation> @@ -2179,23 +2169,48 @@ Adresse: %4 <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accepter kommandolinje- og JSON-RPC-kommandoer </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Kør i baggrunden som en service, og acceptér kommandoer </translation> @@ -2207,7 +2222,7 @@ Adresse: %4 </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Acceptér forbindelser udefra (default: 1 if no -proxy or -connect)</translation> </message> @@ -2232,12 +2247,7 @@ Adresse: %4 <translation>Advarsel: Undersøg venligst at din computers dato og klokkeslet er korrekt indstillet! Hvis der er fejl i disse vil Bitcoin ikke fungere korrekt.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2262,17 +2272,12 @@ Adresse: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Ugyldig -tor address: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2379,30 +2384,24 @@ Adresse: %4 </translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Lyt til JSON-RPC-forbindelser på <port> (standard: 8332) -</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Tillad JSON-RPC-forbindelser fra bestemt IP-adresse </translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Send kommandoer til node, der kører på <ip> (standard: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2429,12 +2428,12 @@ Adresse: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Brug OpenSSL (https) for JSON-RPC-forbindelser </translation> @@ -2452,24 +2451,24 @@ Adresse: %4 </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Acceptabele ciphers (standard: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) </translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Denne hjælpebesked </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Kan låse data-biblioteket %s. Bitcoin kører sikkert allerede.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2484,12 +2483,12 @@ Adresse: %4 <translation>Tilslut via SOCKS proxy</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Tillad DNS-opslag for addnode og connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Indlæser adresser...</translation> </message> @@ -2519,12 +2518,12 @@ Adresse: %4 <translation>Fejl ved indlæsning af wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Ugyldig -proxy adresse: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2544,12 +2543,12 @@ Adresse: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Ugyldigt beløb for -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2559,12 +2558,12 @@ Adresse: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Denne transaktion er over størrelsesbegrænsningen. Du kan stadig sende den for et gebyr på %1 som går til de noder der behandler din transaktion, og som hjælper med at støtte netværket. Ønsker du at betale gebyret?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Fejl: Oprettelse af transaktionen mislykkedes </translation> </message> @@ -2574,12 +2573,12 @@ Adresse: %4 <translation>Sender...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Fejl: Transaktionen blev afvist. Dette kan ske hvis nogle af dine coins i din tegnebog allerede var brugt, som hvis du brugte en kopi af wallet.dat og dine coins er blevet brugt i kopien, men ikke er markeret som brugt her.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Ugyldigt beløb</translation> </message> @@ -2589,12 +2588,12 @@ Adresse: %4 <translation>Du har ikke penge nok</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Indlæser blok-indeks...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Tilføj en node til at forbinde til and attempt to keep the connection open</translation> </message> @@ -2604,7 +2603,7 @@ Adresse: %4 <translation>Kunne ikke binde sig til %s på denne computer. Bitcoin kører sikkert allerede.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2649,7 +2648,7 @@ Adresse: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2661,12 +2660,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation type="unfinished"/> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts index f029b1982b..f30a5ec54f 100644 --- a/src/qt/locale/bitcoin_de.ts +++ b/src/qt/locale/bitcoin_de.ts @@ -10,7 +10,7 @@ <message> <location line="+39"/> <source><b>Bitcoin</b> version</source> - <translation><b>Bitcoin</b> Version</translation> + <translation><b>Bitcoin</b>-Version</translation> </message> <message> <location line="+41"/> @@ -28,9 +28,9 @@ This product includes software developed by the OpenSSL Project for use in the O <translation> Dies ist experimentelle Software. -Veröffentlicht unter der MIT/X11 Software-Lizenz, siehe beiligende Datei COPYING oder http://www.opensource.org/licenses/mit-license.php. +Veröffentlicht unter der MIT/X11-Softwarelizenz, siehe beiligende Datei COPYING oder http://www.opensource.org/licenses/mit-license.php. -Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im OpenSSL Toolkit (http://www.openssl.org/) entwickelt wurde, sowie kryptographische Software geschrieben von Eric Young (eay@cryptsoft.com) und UPnP Software geschrieben von Thomas Bernard.</translation> +Dieses Produkt enthält Software, die vom OpenSSL-Projekt zur Verwendung im OpenSSL-Toolkit (http://www.openssl.org/) entwickelt wurde, sowie kryptographische Software geschrieben von Eric Young (eay@cryptsoft.com) und UPnP-Software geschrieben von Thomas Bernard.</translation> </message> </context> <context> @@ -82,11 +82,16 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation>Nachricht &signieren</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>Die ausgewählte Adresse aus der Liste entfernen.</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Eine Nachricht verifizieren, um sicherzustellen, dass diese mit einer angegebenen Bitcoin-Adresse signiert wurde</translation> </message> @@ -96,12 +101,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>Nachricht &verifizieren</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Die ausgewählte Adresse aus der Liste entfernen. Sie können nur Zahlungsadressen entfernen.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Löschen</translation> </message> @@ -159,7 +159,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <message> <location filename="../forms/askpassphrasedialog.ui" line="+26"/> <source>Passphrase Dialog</source> - <translation>Passphrasen Dialog</translation> + <translation>Passphrasendialog</translation> </message> <message> <location line="+21"/> @@ -214,7 +214,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <message> <location line="+1"/> <source>Enter the old and new passphrase to the wallet.</source> - <translation>Geben Sie die alte und die neue Passphrase der Brieftasche ein.</translation> + <translation>Geben Sie die alte und neue Passphrase der Brieftasche ein.</translation> </message> <message> <location line="+46"/> @@ -224,7 +224,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <message> <location line="+1"/> <source>Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>!</source> - <translation>Warnung: Wenn Sie Ihre Brieftasche verschlüsseln und Ihre Passphrase verlieren, werden Sie <b>ALLE IHRE BITCOINS VERLIEREN</b>!</translation> + <translation>Warnung: Wenn Sie Ihre Brieftasche verschlüsseln und Ihre Passphrase verlieren, werden Sie <b>alle Ihre Bitcoins verlieren</b>!</translation> </message> <message> <location line="+0"/> @@ -232,24 +232,29 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>Sind Sie sich sicher, dass Sie Ihre Brieftasche verschlüsseln möchten?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>WICHTIG: Alle vorherigen Sicherungen Ihrer Brieftasche sollten durch die neu erzeugte, verschlüsselte Brieftasche ersetzt werden. Aus Sicherheitsgründen werden vorherige Sicherungen der unverschlüsselten Brieftasche nutzlos, sobald Sie die neue, verschlüsselte Brieftasche verwenden.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Warnung: Die Feststelltaste ist aktiviert!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Brieftasche verschlüsselt</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin wird jetzt beendet, um den Verschlüsselungsprozess abzuschließen. Bitte beachten Sie, dass die Verschlüsselung Ihrer Brieftasche nicht vollständig vor Diebstahl Ihrer Bitcoins durch Schadsoftware schützt, die Ihren Computer befällt.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Nachricht s&ignieren...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synchronisiere mit Netzwerk...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Übersicht</translation> </message> @@ -313,7 +318,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>Allgemeine Übersicht der Brieftasche anzeigen</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transaktionen</translation> </message> @@ -333,7 +338,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>Liste der gespeicherten Zahlungsadressen und Bezeichnungen bearbeiten</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>Bitcoins &empfangen</translation> </message> @@ -343,12 +348,12 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>Liste der Empfangsadressen anzeigen</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>Bitcoins &überweisen</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Beenden</translation> </message> @@ -378,7 +383,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>&Erweiterte Einstellungen...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>Brieftasche &verschlüsseln...</translation> </message> @@ -403,42 +408,27 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>%1 von %2 Blöcken des Transaktionsverlaufs heruntergeladen (%3% fertig).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exportieren...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Bitcoins an eine Bitcoin-Adresse überweisen</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Eine Nachricht signieren, um den Besitz einer Bitcoin-Adresse zu beweisen</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Eine Nachricht verifizieren, um sicherzustellen, dass diese mit einer angegebenen Bitcoin-Adresse signiert wurde</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>S&ignaturen</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Erweiterte Bitcoin-Einstellungen ändern</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Daten der aktuellen Ansicht in eine Datei exportieren</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Brieftasche ent- oder verschlüsseln</translation> </message> @@ -453,7 +443,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>Ändert die Passphrase, die für die Verschlüsselung der Brieftasche benutzt wird</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Debugfenster</translation> </message> @@ -463,12 +453,12 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>Debugging- und Diagnosekonsole öffnen</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>Nachricht &verifizieren...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,7 +468,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>Brieftasche</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Über Bitcoin</translation> </message> @@ -488,12 +478,12 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>&Anzeigen / Verstecken</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Datei</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Einstellungen</translation> </message> @@ -508,7 +498,7 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <translation>Registerkartenleiste</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Aktionssymbolleiste</translation> </message> @@ -522,10 +512,10 @@ Dieses Produkt enthält Software, die vom OpenSSL Projekt zur Verwendung im Open <location line="+0"/> <location line="+60"/> <source>Bitcoin client</source> - <translation>Bitcoin Client</translation> + <translation>Bitcoin-Client</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktive Verbindung zum Bitcoin-Netzwerk</numerusform><numerusform>%n aktive Verbindungen zum Bitcoin-Netzwerk</numerusform></translation> </message> @@ -602,7 +592,7 @@ Typ: %3 Adresse: %4</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>URI Verarbeitung</translation> @@ -819,7 +809,7 @@ Adresse: %4</translation> <message> <location line="+6"/> <source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source> - <translation>Automatisch den Bitcoin Clientport auf dem Router öffnen. Dies funktioniert nur, wenn Ihr Router UPnP unterstützt und dies aktiviert ist.</translation> + <translation>Automatisch den Bitcoin-Clientport auf dem Router öffnen. Dies funktioniert nur, wenn Ihr Router UPnP unterstützt und dies aktiviert ist.</translation> </message> <message> <location line="+3"/> @@ -975,7 +965,7 @@ Adresse: %4</translation> <location line="+33"/> <location line="+183"/> <source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source> - <translation>Die angezeigten Informationen sind möglicherweise nicht mehr aktuell. Ihre Brieftasche wird synchronisiert nachdem eine Verbindung zum Netzwerk hergestellt wurde, dieser Prozess ist aber derzeit noch nicht abgeschlossen.</translation> + <translation>Die angezeigten Informationen sind möglicherweise nicht mehr aktuell. Ihre Brieftasche wird automatisch synchronisiert, nachdem eine Verbindung zum Bitcoin-Netzwerk hergestellt wurde. Dieser Prozess ist jedoch derzeit noch nicht abgeschlossen.</translation> </message> <message> <location line="-141"/> @@ -1039,7 +1029,7 @@ Adresse: %4</translation> <message> <location filename="../forms/qrcodedialog.ui" line="+14"/> <source>QR Code Dialog</source> - <translation>QR-Code Dialog</translation> + <translation>QR-Code-Dialog</translation> </message> <message> <location line="+59"/> @@ -1109,7 +1099,7 @@ Adresse: %4</translation> <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>n.v.</translation> </message> @@ -1126,7 +1116,7 @@ Adresse: %4</translation> <message> <location line="+68"/> <source>Using OpenSSL version</source> - <translation>Verwendete OpenSSL Version</translation> + <translation>Verwendete OpenSSL-Version</translation> </message> <message> <location line="+49"/> @@ -1181,7 +1171,7 @@ Adresse: %4</translation> <message> <location line="+7"/> <source>Show the Bitcoin-Qt help message to get a list with possible Bitcoin command-line options.</source> - <translation>Zeige die Bitcoin-Qt Hilfsnachricht, um eine Liste mit möglichen Kommandozeilenoptionen zu erhalten.</translation> + <translation>Zeige die Bitcoin-Qt-Hilfsnachricht, um eine Liste mit möglichen Kommandozeilenoptionen zu erhalten.</translation> </message> <message> <location line="+3"/> @@ -1206,7 +1196,7 @@ Adresse: %4</translation> <message> <location line="+25"/> <source>Bitcoin Core</source> - <translation>Bitcoinkern</translation> + <translation>Bitcoin-Kern</translation> </message> <message> <location line="+279"/> @@ -1216,7 +1206,7 @@ Adresse: %4</translation> <message> <location line="+7"/> <source>Open the Bitcoin debug log file from the current data directory. This can take a few seconds for large log files.</source> - <translation>Öffnet die Bitcoin Debugprotokolldatei aus dem aktuellen Datenverzeichnis. Dies kann bei großen Protokolldateien einige Sekunden dauern.</translation> + <translation>Öffnet die Bitcoin-Debugprotokolldatei aus dem aktuellen Datenverzeichnis. Dies kann bei großen Protokolldateien einige Sekunden dauern.</translation> </message> <message> <location line="+102"/> @@ -1226,7 +1216,7 @@ Adresse: %4</translation> <message> <location filename="../rpcconsole.cpp" line="-33"/> <source>Welcome to the Bitcoin RPC console.</source> - <translation>Willkommen in der Bitcoin RPC-Konsole.</translation> + <translation>Willkommen in der Bitcoin-RPC-Konsole.</translation> </message> <message> <location line="+1"/> @@ -1260,7 +1250,7 @@ Adresse: %4</translation> </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation>Empfänger &hinzufügen</translation> </message> <message> @@ -1290,7 +1280,7 @@ Adresse: %4</translation> </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation>&Überweisen</translation> </message> <message> @@ -1646,7 +1636,7 @@ Adresse: %4</translation> <translation>Generiert</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Von</translation> @@ -1757,12 +1747,12 @@ Adresse: %4</translation> <translation>falsch</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, wurde noch nicht erfolgreich übertragen</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>unbekannt</translation> </message> @@ -2073,9 +2063,9 @@ Adresse: %4</translation> <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> - <translation>Bitcoin Version</translation> + <translation>Bitcoin-Version</translation> </message> <message> <location line="+82"/> @@ -2088,12 +2078,12 @@ Adresse: %4</translation> <translation>Befehl an -server oder bitcoind senden</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Befehle auflisten</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Hilfe zu einem Befehl erhalten</translation> </message> @@ -2168,22 +2158,47 @@ Adresse: %4</translation> <translation>Schwellenwert, um Verbindungen zu sich nicht konform verhaltenden Gegenstellen zu beenden (Standard: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Anzahl Sekunden, während denen sich nicht konform verhaltenden Gegenstellen die Wiederverbindung verweigert wird (Standard: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>Beim Einrichten des abzuhörenden RPC-Ports %i für IPv6 ist ein Fehler aufgetreten, daher wird auf IPv4 zurückgegriffen: %s</translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>Beim Einrichten des abzuhörenden RPC-Ports %u für IPv4 ist ein Fehler aufgetreten: %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Block- und Adressdatenbank beim Beenden trennen. Verlängert, die zum Beenden benötigte Zeit (Standard: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation><port> nach JSON-RPC-Verbindungen abhören (Standard: 8332 oder Testnetz: 18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> - <translation>Kommandozeilenbefehle und JSON-RPC Befehle annehmen</translation> + <translation>Kommandozeilenbefehle und JSON-RPC-Befehle annehmen</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>Importiere Datendatei der Blockkette...</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>Importiere Bootstrap-Datendatei der Blockkette...</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Als Hintergrunddienst starten und Befehle annehmen</translation> </message> @@ -2193,14 +2208,14 @@ Adresse: %4</translation> <translation>Das Testnetz verwenden</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Eingehende Verbindungen annehmen (Standard: 1, wenn nicht -proxy oder -connect)</translation> </message> <message> <location line="-20"/> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)</source> - <translation>Maximale Größe von high-priority/low-fee Transaktionen in Byte festlegen (Standard: 27000)</translation> + <translation>Maximale Größe von "high-priority/low-fee"-Transaktionen in Byte festlegen (Standard: 27000)</translation> </message> <message> <location line="+5"/> @@ -2218,12 +2233,7 @@ Adresse: %4</translation> <translation>Warnung: Bitte korrigieren Sie die Datums- und Uhrzeiteinstellungen Ihres Computers, da Bitcoin ansonsten nicht ordnungsgemäß funktionieren wird!</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Beim Einrichten des abzuhörenden RPC-Ports %i ist ein Fehler aufgetreten: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Blockerzeugungsoptionen:</translation> </message> @@ -2248,17 +2258,12 @@ Adresse: %4</translation> <translation>Gegenstellen via DNS-Namensauflösung finden (Standard: 1, außer bei -connect)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Importiere Blöcke...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Ungültige Adresse in -tor: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Maximale Größe, <n> * 1000 Byte, des Empfangspuffers pro Verbindung (Standard: 5000)</translation> </message> @@ -2290,7 +2295,7 @@ Adresse: %4</translation> <message> <location line="+4"/> <source>SSL options: (see the Bitcoin Wiki for SSL setup instructions)</source> - <translation>SSL-Optionen: (siehe Bitcoin-Wiki für SSL Installationsanweisungen)</translation> + <translation>SSL-Optionen: (siehe Bitcoin-Wiki für SSL-Installationsanweisungen)</translation> </message> <message> <location line="+1"/> @@ -2345,7 +2350,7 @@ Adresse: %4</translation> <message> <location line="+2"/> <source>Username for JSON-RPC connections</source> - <translation>Benutzername für JSON-RPC Verbindungen</translation> + <translation>Benutzername für JSON-RPC-Verbindungen</translation> </message> <message> <location line="+2"/> @@ -2360,30 +2365,25 @@ Adresse: %4</translation> <message> <location line="-41"/> <source>Password for JSON-RPC connections</source> - <translation>Passwort für JSON-RPC Verbindungen</translation> + <translation>Passwort für JSON-RPC-Verbindungen</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation><port> nach JSON-RPC Verbindungen abhören (Standard: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> - <translation>JSON-RPC Verbindungen von der angegebenen IP-Adresse erlauben</translation> + <translation>JSON-RPC-Verbindungen von der angegebenen IP-Adresse erlauben</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Sende Befehle an Knoten <ip> (Standard: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Kommando ausführen wenn der beste Block wechselt (%s im Kommando wird durch den Hash des Blocks ersetzt)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Brieftasche auf das neueste Format aktualisieren</translation> </message> @@ -2408,14 +2408,14 @@ Adresse: %4</translation> <translation>Wie gründlich soll die Blockprüfung sein (0-6, Standard: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Blöcke aus einer externen blk000?.dat Datei importieren</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> - <translation>OpenSSL (https) für JSON-RPC Verbindungen verwenden</translation> + <translation>OpenSSL (https) für JSON-RPC-Verbindungen verwenden</translation> </message> <message> <location line="-21"/> @@ -2428,22 +2428,22 @@ Adresse: %4</translation> <translation>Privater Serverschlüssel (Standard: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Akzeptierte Chiffren (Standard: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Dieser Hilfetext</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Datenverzeichnis %s kann nicht gesperrt werden. Evtl. wurde Bitcoin bereits gestartet.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2458,12 +2458,12 @@ Adresse: %4</translation> <translation>Über einen SOCKS-Proxy verbinden</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Erlaube DNS-Namensauflösung für -addnode, -seednode und -connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Lade Adressen...</translation> </message> @@ -2493,12 +2493,12 @@ Adresse: %4</translation> <translation>Fehler beim Laden von wallet.dat (Brieftasche)</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Ungültige Adresse in -proxy: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Unbekannter Netztyp in -onlynet angegeben: '%s'</translation> </message> @@ -2518,12 +2518,12 @@ Adresse: %4</translation> <translation>Kann Adresse in -externalip nicht auflösen: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Falscher Betrag für -paytxfee=<Betrag>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Fehler: Knoten konnte nicht gestartet weden</translation> </message> @@ -2533,12 +2533,12 @@ Adresse: %4</translation> <translation>Fehler: Brieftasche gesperrt, Transaktion kann nicht erstellt werden</translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Fehler: Diese Transaktion benötigt aufgrund ihres Betrags, ihrer Komplexität oder der Nutzung kürzlich erhaltener Zahlungen eine Transaktionsgebühr in Höhe von mindestens %s.</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Fehler: Transaktionserstellung fehlgeschlagen</translation> </message> @@ -2548,12 +2548,12 @@ Adresse: %4</translation> <translation>Senden...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Fehler: Die Transaktion wurde abgelehnt. Dies kann passieren, wenn einige Bitcoins aus Ihrer Brieftasche bereits ausgegeben wurden. Beispielsweise weil Sie eine Kopie Ihrer wallet.dat genutzt, die Bitcoins dort ausgegeben haben und dies daher in der derzeit aktiven Brieftasche nicht vermerkt ist.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Ungültige Angabe</translation> </message> @@ -2563,12 +2563,12 @@ Adresse: %4</translation> <translation>Unzureichender Kontostand</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Lade Blockindex...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Mit dem Knoten verbinden und versuchen die Verbindung aufrecht zu halten</translation> </message> @@ -2578,7 +2578,7 @@ Adresse: %4</translation> <translation>Kann auf diesem Computer nicht an %s binden. Evtl. wurde Bitcoin bereits gestartet.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Gegenstellen via Internet Relay Chat finden (Standard: 0)</translation> </message> @@ -2623,7 +2623,7 @@ Adresse: %4</translation> <translation>Zur Nutzung der %s Option</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2642,12 +2642,12 @@ Falls die Konfigurationsdatei nicht existiert, erzeugen Sie diese bitte mit Lese </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Fehler</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_el_GR.ts b/src/qt/locale/bitcoin_el_GR.ts index b3c8aa589a..2291c850bc 100644 --- a/src/qt/locale/bitcoin_el_GR.ts +++ b/src/qt/locale/bitcoin_el_GR.ts @@ -77,11 +77,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Υπέγραψε το μήνυμα</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -91,12 +96,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Διέγραψε την επιλεγμένη διεύθυνση από την λίστα. Μόνο διευθύνσεις αποστολής μπορούν να διαγραφούν.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Διαγραφή</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Κρυπτογραφημενο πορτοφολι</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Το Bitcoin θα κλεισει τώρα για να τελειώσει την διαδικασία κρυπτογραφησης. Θυμησου ότι κρυπτογραφώντας το πορτοφολι σου δεν μπορείς να προστατέψεις πλήρως τα bitcoins σου από κλοπή στην περίπτωση όπου μολυνθεί ο υπολογιστής σου με κακόβουλο λογισμικο.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Υπογραφή &Μηνύματος...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Συγχρονισμός με το δίκτυο...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Επισκόπηση</translation> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Εμφάνισε γενική εικονα του πορτοφολιού</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Συναλλαγές</translation> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Εξεργασια της λιστας των αποθηκευμενων διευθύνσεων και ετικετων</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Παραλαβή νομισματων</translation> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Εμφάνισε την λίστα των διευθύνσεων για την παραλαβή πληρωμων</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Αποστολη νομισματων</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>Έ&ξοδος</translation> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Επιλογές...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Κρυπτογράφησε το πορτοφόλι</translation> </message> @@ -398,42 +403,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Κατέβηκαν %1 από %2 μπλοκ του ιστορικού συναλλαγών (%3% ολοκληρώθηκαν)</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Εξαγωγή</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Υπογράψτε ένα μήνυμα για ν' αποδείξετε πως σας ανήκει μια συγκεκριμένη διεύθυνση Bitcoin</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation type="unfinished"/> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Εξαγωγή δεδομένων καρτέλας σε αρχείο</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Κρυπτογράφηση ή αποκρυπτογράφηση πορτοφολιού</translation> </message> @@ -448,7 +438,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Αλλαγή του κωδικού κρυπτογράφησης του πορτοφολιού</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -458,12 +448,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -473,7 +463,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Πορτοφόλι</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Σχετικα:Bitcoin</translation> </message> @@ -483,12 +473,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Εμφάνισε/Κρύψε</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Αρχείο</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Ρυθμίσεις</translation> </message> @@ -503,7 +493,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Εργαλειοθήκη καρτελών</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Εργαλειοθήκη ενεργειών</translation> </message> @@ -520,7 +510,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Πελάτης Bitcoin</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n ενεργή σύνδεση στο δίκτυο Bitcoin</numerusform><numerusform>%n ενεργές συνδέσεις στο δίκτυο Βitcoin</numerusform></translation> </message> @@ -600,7 +590,7 @@ Address: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1107,7 +1097,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1258,7 +1248,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation type="unfinished"/> </message> <message> @@ -1288,8 +1278,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Αποστολή</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1645,7 +1635,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation type="unfinished"/> @@ -1756,12 +1746,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, δεν έχει ακόμα μεταδοθεί μ' επιτυχία</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>άγνωστο</translation> </message> @@ -2072,7 +2062,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Έκδοση Bitcoin</translation> </message> @@ -2087,12 +2077,12 @@ Address: %4 <translation>Αποστολή εντολής στον εξυπηρετητή ή στο bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Λίστα εντολών</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Επεξήγηση εντολής</translation> </message> @@ -2167,22 +2157,47 @@ Address: %4 <translation>Όριο αποσύνδεσης προβληματικών peers (προεπιλογή: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Δευτερόλεπτα πριν επιτραπεί ξανά η σύνδεση των προβληματικών peers (προεπιλογή: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Αποσύνδεση των βάσεων δεδομένων μπλοκ και διευθύνσεων. Αυξάνει το χρόνο κλεισίματος (προεπιλογή: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Αποδοχή εντολών κονσόλας και JSON-RPC</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Εκτέλεση στο παρασκήνιο κι αποδοχή εντολών</translation> </message> @@ -2192,7 +2207,7 @@ Address: %4 <translation>Χρήση του δοκιμαστικού δικτύου</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2217,12 +2232,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2247,17 +2257,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2362,27 +2367,22 @@ Address: %4 <translation>Κωδικός για τις συνδέσεις JSON-RPC</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Εισερχόμενες συνδέσεις JSON-RPC στη θύρα <port> (προεπιλογή: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Αποδοχή συνδέσεων JSON-RPC από συγκεκριμένη διεύθυνση IP</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Αποστολή εντολών στον κόμβο <ip> (προεπιλογή: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Εκτέλεσε την εντολή όταν το καλύτερο μπλοκ αλλάξει(%s στην εντολή αντικαθίσταται από το hash του μπλοκ)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Αναβάθμισε το πορτοφόλι στην τελευταία έκδοση</translation> </message> @@ -2407,12 +2407,12 @@ Address: %4 <translation>Πόσο εξονυχιστική να είναι η επιβεβαίωση του μπλοκ(0-6, προεπιλογή:1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Εισαγωγή μπλοκ από εξωτερικό αρχείο blk000?.dat</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Χρήση του OpenSSL (https) για συνδέσεις JSON-RPC</translation> </message> @@ -2427,22 +2427,22 @@ Address: %4 <translation>Προσωπικό κλειδί του διακομιστή (προεπιλογή: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Αποδεκτά κρυπτογραφήματα (προεπιλογή: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Αυτό το κείμενο βοήθειας</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Αδυναμία κλειδώματος του φακέλου δεδομένων %s. Πιθανώς το Bitcoin να είναι ήδη ενεργό.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2457,12 +2457,12 @@ Address: %4 <translation>Σύνδεση μέσω διαμεσολαβητή socks</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Φόρτωση διευθύνσεων...</translation> </message> @@ -2492,12 +2492,12 @@ Address: %4 <translation>Σφάλμα φόρτωσης αρχείου wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Δεν είναι έγκυρη η διεύθυνση διαμεσολαβητή: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2517,12 +2517,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2532,12 +2532,12 @@ Address: %4 <translation>Σφάλμα: το πορτοφόλι είναι κλειδωμένο, δεν μπορεί να δημιουργηθεί συναλλαγή</translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Σφάλμα: Αυτή η συναλλαγή απαιτεί αμοιβή συναλλαγής τουλάχιστον %s λόγω του μεγέθους, πολυπλοκότητας ή της χρήσης πρόσφατης παραλαβής κεφαλαίου</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Σφάλμα: Η δημιουργία της συναλλαγής απέτυχε</translation> </message> @@ -2547,13 +2547,13 @@ Address: %4 <translation>Αποστολή...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Σφάλμα: Η συναλλαγή απορρίφθηκε. Αυτό ίσως οφείλεται στο ότι τα νομίσματά σας έχουν ήδη ξοδευτεί, π.χ. με την αντιγραφή του wallet.dat σε άλλο σύστημα και την χρήση τους εκεί, χωρίς η συναλλαγή να έχει καταγραφεί στο παρόν σύστημα.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Λάθος ποσότητα</translation> </message> @@ -2563,12 +2563,12 @@ Address: %4 <translation>Ανεπαρκές κεφάλαιο</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Φόρτωση ευρετηρίου μπλοκ...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Προσέθεσε ένα κόμβο για σύνδεση και προσπάθησε να κρατήσεις την σύνδεση ανοιχτή</translation> </message> @@ -2578,7 +2578,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Βρες ομότιμους υπολογιστές χρησιμοποιώντας internet relay chat(Προεπιλογή:0)</translation> </message> @@ -2623,7 +2623,7 @@ Address: %4 <translation>Χρήση της %s επιλογής</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2640,12 +2640,12 @@ rpcpassword=%s Εάν το αρχείο δεν υπάρχει, δημιούργησε το με δικαιώματα μόνο για ανάγνωση από τον δημιουργό.</translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Σφάλμα</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 715b046a83..ee9967e15d 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -85,12 +85,12 @@ This product includes software developed by the OpenSSL Project for use in the O <message> <location line="+3"/> <source>Sign &Message</source> - <translation type="unfinished"></translation> + <translation>Sign &Message</translation> </message> <message> <location line="+25"/> <source>Delete the currently selected address from the list</source> - <translation type="unfinished"></translation> + <translation>Delete the currently selected address from the list</translation> </message> <message> <location line="-14"/> @@ -236,7 +236,7 @@ This product includes software developed by the OpenSSL Project for use in the O <message> <location line="+15"/> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> - <translation type="unfinished"></translation> + <translation>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</translation> </message> <message> <location line="+100"/> @@ -1272,7 +1272,7 @@ Address: %4 <message> <location line="+3"/> <source>Add &Recipient</source> - <translation type="unfinished"></translation> + <translation>Add &Recipient</translation> </message> <message> <location line="+20"/> @@ -1302,7 +1302,7 @@ Address: %4 <message> <location line="+3"/> <source>S&end</source> - <translation type="unfinished"></translation> + <translation>S&end</translation> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -2199,14 +2199,9 @@ Address: %4 <translation>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</translation> </message> <message> - <location line="-22"/> - <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> + <location line="-19"/> <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> - <translation type="unfinished"></translation> + <translation>An error occurred while setting up the RPC port %u for listening on IPv4: %s</translation> </message> <message> <location line="+5"/> @@ -2216,7 +2211,7 @@ Address: %4 <message> <location line="+12"/> <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> - <translation type="unfinished"></translation> + <translation>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</translation> </message> <message> <location line="+24"/> @@ -2226,12 +2221,12 @@ Address: %4 <message> <location line="+36"/> <source>Importing blockchain data file.</source> - <translation type="unfinished"></translation> + <translation>Importing blockchain data file.</translation> </message> <message> <location line="+1"/> <source>Importing bootstrap blockchain data file.</source> - <translation type="unfinished"></translation> + <translation>Importing bootstrap blockchain data file.</translation> </message> <message> <location line="+23"/> @@ -2249,7 +2244,12 @@ Address: %4 <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> <message> - <location line="-20"/> + <location line="-45"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s</source> + <translation>An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s</translation> + </message> + <message> + <location line="+25"/> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)</source> <translation>Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)</translation> </message> diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts index 25d8b801a6..75e1d20e8f 100644 --- a/src/qt/locale/bitcoin_es.ts +++ b/src/qt/locale/bitcoin_es.ts @@ -85,11 +85,16 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation>&Firmar mensaje</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>Borrar de la lista la dirección seleccionada</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Verifique un mensaje para comprobar que fue firmado con la dirección Bitcoin indicada</translation> </message> @@ -99,12 +104,7 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>&Verificar mensaje</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Borrar de la lista la dirección seleccionada . Sólo se pueden borrar las direcciones de envío.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Borrar</translation> </message> @@ -235,24 +235,29 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>¿Seguro que desea cifrar su monedero?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>IMPORTANTE: Cualquier copia de seguridad que haya realizado previamente de su fichero de billetera debe reemplazarse con el fichero de billetera encriptado recientemente creado. Por razones de seguridad, las copias de seguridad previas del fichero de billetera que no estaban encriptadas pasarán a estar inservibles en cuanto comience a usar la nueva billetera encriptada.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Aviso: ¡La tecla de bloqueo de mayúsculas está activada!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Monedero cifrado</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin se cerrará para finalizar el proceso de cifrado. Recuerde que el cifrado de su monedero no puede proteger totalmente sus bitcoins de robo por malware que infecte su sistema.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -296,17 +301,17 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Firmar &mensaje...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Sincronizando con la red…</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Vista general</translation> </message> @@ -316,7 +321,7 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Mostrar vista general del monedero</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transacciones</translation> </message> @@ -336,7 +341,7 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Editar la lista de las direcciones y etiquetas almacenadas</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Recibir monedas</translation> </message> @@ -346,12 +351,12 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Mostrar la lista de direcciones utilizadas para recibir pagos</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Enviar monedas</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Salir</translation> </message> @@ -381,7 +386,7 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>&Opciones...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Cifrar monedero…</translation> </message> @@ -406,42 +411,27 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Descargados %1 de %2 bloques del historial de transacciones (%3% hecho).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exportar…</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Enviar monedas a una dirección Bitcoin</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Firme un mensaje para demostrar que posee una dirección Bitcoin</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Verifique un mensaje para comprobar que fue firmado con la dirección Bitcoin indicada</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>F&irmas</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Modificar las opciones de configuración de Bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Exportar a un archivo los datos de esta pestaña</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Cifrar o descifrar el monedero</translation> </message> @@ -456,7 +446,7 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Cambiar la contraseña utilizada para el cifrado del monedero</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>Ventana de &depuración</translation> </message> @@ -466,12 +456,12 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Abrir la consola de depuración y diagnóstico</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Verificar mensaje</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -481,7 +471,7 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Monedero</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Acerca de Bitcoin</translation> </message> @@ -491,12 +481,12 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Mo&strar/ocultar</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Archivo</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Configuración</translation> </message> @@ -511,7 +501,7 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Barra de pestañas</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Barra de acciones</translation> </message> @@ -528,7 +518,7 @@ Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard.</t <translation>Cliente Bitcoin</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n conexión activa hacia la red Bitcoin</numerusform><numerusform>%n conexiones activas hacia la red Bitcoin</numerusform></translation> </message> @@ -606,7 +596,7 @@ Dirección: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>Gestión de URI</translation> @@ -1113,7 +1103,7 @@ Dirección: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>N/D</translation> </message> @@ -1264,7 +1254,7 @@ Dirección: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation>&Añadir destinatario</translation> </message> <message> @@ -1294,7 +1284,7 @@ Dirección: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation>&Enviar</translation> </message> <message> @@ -1650,7 +1640,7 @@ Dirección: %4 <translation>Generado</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>De</translation> @@ -1761,12 +1751,12 @@ Dirección: %4 <translation>falso</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, no ha sido emitido satisfactoriamente todavía</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>desconocido</translation> </message> @@ -2077,7 +2067,7 @@ Dirección: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Versión de Bitcoin</translation> </message> @@ -2092,13 +2082,13 @@ Dirección: %4 <translation>Envíar comando a -server o bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Muestra comandos </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Recibir ayuda para un comando </translation> @@ -2177,23 +2167,48 @@ Dirección: %4 <translation>Umbral para la desconexión de pares con mal comportamiento (predeterminado: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Número de segundos en que se evita la reconexión de pares con mal comportamiento (predeterminado: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>Ha ocurrido un error al establecer el puerto %i para escucha por RPC en IPv6. Se ha retornado al modo IPv4: %s </translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>Ha ocurrido un error al configurar el puerto RPC %u para escucha en IPv4: %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Desconectar las bases de datos de bloques y direcciones al cerrar la aplicación. El tiempo de parada de la aplicación aumentará (predeterminado: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>Escuchar conexiones JSON-RPC en <puerto> (predeterminado: 8332 o testnet:18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Aceptar comandos consola y JSON-RPC </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>Importando fichero de datos de cadena de bloques.</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>Importando fichero de datos para iniciar la cadena de bloques.</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Correr como demonio y aceptar comandos </translation> @@ -2205,7 +2220,7 @@ Dirección: %4 </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Aceptar conexiones desde el exterior (predeterminado: 1 si no -proxy o -connect)</translation> </message> @@ -2230,12 +2245,7 @@ Dirección: %4 <translation>Precaución: Por favor, ¡revise que la fecha y hora de su ordenador son correctas! Si su reloj está mal, Bitcoin no funcionará correctamente.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Ha ocurrido un error al configurar el puerto RPC %i para escuchar: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Opciones de creación de bloques:</translation> </message> @@ -2260,17 +2270,12 @@ Dirección: %4 <translation>Encontrar pares mediante búsqueda de DNS (predeterminado: 1 salvo con -connect)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Importando bloques...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Dirección -tor inválida: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Búfer de recepción máximo por conexión, <n>*1000 bytes (predeterminado: 5000)</translation> </message> @@ -2377,30 +2382,24 @@ Dirección: %4 </translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Escuchar conexiones JSON-RPC en el puerto <port> (predeterminado: 8332) -</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Permitir conexiones JSON-RPC desde la dirección IP especificada </translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Enviar comando al nodo situado en <ip> (predeterminado: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Ejecutar un comando cuando cambia el mejor bloque (%s en cmd se sustituye por el hash de bloque)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Actualizar el monedero al último formato</translation> </message> @@ -2426,12 +2425,12 @@ Dirección: %4 <translation>Nivel de detalle en la verificación de bloques (0-6, predeterminado: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Importa los bloques desde un archivo externo blk000?.dat</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Usar OpenSSL (https) para las conexiones JSON-RPC </translation> @@ -2449,25 +2448,25 @@ Dirección: %4 </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Cifrados aceptados (predeterminado: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) </translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Este mensaje de ayuda </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>No se puede obtener permiso de trabajo en la carpeta de datos %s. Probablemente Bitcoin ya se está ejecutando. </translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2482,12 +2481,12 @@ Dirección: %4 <translation>Conectar mediante proxy socks</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Permitir búsquedas DNS para -addnode, -seednode y -connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Cargando direcciones...</translation> </message> @@ -2517,12 +2516,12 @@ Dirección: %4 <translation>Error al cargar wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Dirección -proxy inválida: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>La red especificada en -onlynet '%s' es desconocida</translation> </message> @@ -2542,12 +2541,12 @@ Dirección: %4 <translation>No se puede resolver la dirección de -externalip: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Cantidad inválida para -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Error: no se ha podido iniciar el nodo</translation> </message> @@ -2557,12 +2556,12 @@ Dirección: %4 <translation>Error: monedero bloqueado. Bitcoin es incapaz de crear la transacción </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Error: esta transacción está sujeta a una tarifa de %s, bien por su cantidad, complejidad, o por el uso de fondos recientemente recibidos </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Error: no se ha podido crear la transacción</translation> </message> @@ -2572,12 +2571,12 @@ Dirección: %4 <translation>Enviando...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Error: la transacción fue rechazada. Esto puede pasar si alguna de las monedas ya estaba gastada o si ha usado una copia de wallet.dat y las monedas se gastaron en la copia pero no se han marcado como gastadas aquí.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Cuantía no válida</translation> </message> @@ -2587,12 +2586,12 @@ Dirección: %4 <translation>Fondos insuficientes</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Cargando el índice de bloques...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Añadir un nodo al que conectarse y tratar de mantener la conexión abierta</translation> </message> @@ -2602,7 +2601,7 @@ Dirección: %4 <translation>No es posible conectar con %s en este sistema. Probablemente Bitcoin ya está ejecutándose.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Encontrar los pares utilizando Internet Relay Chat (predeterminado: 0)</translation> </message> @@ -2647,7 +2646,7 @@ Dirección: %4 <translation>Para utilizar la opción %s</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2665,12 +2664,12 @@ rpcpassword =%s ⏎ Si el archivo no existe, créelo con permiso de lectura solamente del propietario. ⏎</translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Error</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_es_CL.ts b/src/qt/locale/bitcoin_es_CL.ts index 3dd7c56f7e..e4ca53fbc5 100644 --- a/src/qt/locale/bitcoin_es_CL.ts +++ b/src/qt/locale/bitcoin_es_CL.ts @@ -85,11 +85,16 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>Firmar Mensaje</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -99,12 +104,7 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Borra la dirección seleccionada de la lista. Solo las direcciónes de envio se pueden borrar.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Borrar</translation> </message> @@ -235,24 +235,29 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>¿Seguro que quieres seguir codificando la billetera?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Precaucion: Mayúsculas Activadas</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Billetera codificada</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin se cerrará para finalizar el proceso de encriptación. Recuerde que encriptar su billetera no protegera completatamente sus bitcoins de ser robados por malware que infecte su computador</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -296,17 +301,17 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Firmar &Mensaje...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Sincronizando con la red...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Vista general</translation> </message> @@ -316,7 +321,7 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>Muestra una vista general de la billetera</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transacciónes</translation> </message> @@ -336,7 +341,7 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>Edita la lista de direcciones y etiquetas almacenadas</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Recibir monedas</translation> </message> @@ -346,12 +351,12 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>Muestra la lista de direcciónes utilizadas para recibir pagos</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Envíar monedas</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Salir</translation> </message> @@ -381,7 +386,7 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>&Opciones</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Codificar la billetera...</translation> </message> @@ -406,42 +411,27 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>Descargados %1 de %2 bloques del historial de transacciones (%3% hecho).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exportar...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Enviar monedas a una dirección bitcoin</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Firmar un mensaje para provar que usted es dueño de esta dirección</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Modifica las opciones de configuración de bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Exportar los datos de la pestaña actual a un archivo</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Codificar o decodificar la billetera</translation> </message> @@ -456,7 +446,7 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>Cambiar la contraseña utilizada para la codificación de la billetera</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -466,12 +456,12 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -481,7 +471,7 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>Cartera</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Sobre Bitcoin</translation> </message> @@ -491,12 +481,12 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>&Mostrar/Ocultar</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Archivo</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Configuración</translation> </message> @@ -511,7 +501,7 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>Barra de pestañas</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Barra de acciónes</translation> </message> @@ -528,7 +518,7 @@ Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard.</tran <translation>Cliente Bitcoin</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n conexión activa hacia la red Bitcoin</numerusform><numerusform>%n conexiones activas hacia la red Bitcoin</numerusform></translation> </message> @@ -605,7 +595,7 @@ Tipo: %3 Dirección: %4</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1113,7 +1103,7 @@ Dirección: %4</translation> <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1264,8 +1254,8 @@ Dirección: %4</translation> </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Agrega destinatario</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1294,8 +1284,8 @@ Dirección: %4</translation> </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Envía</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1650,7 +1640,7 @@ Dirección: %4</translation> <translation>Generado</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>De</translation> @@ -1761,12 +1751,12 @@ Dirección: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, no ha sido emitido satisfactoriamente todavía</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>desconocido</translation> </message> @@ -2077,7 +2067,7 @@ Dirección: %4</translation> <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Versión Bitcoin</translation> </message> @@ -2093,13 +2083,13 @@ Dirección: %4</translation> </translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Muestra comandos </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Recibir ayuda para un comando </translation> @@ -2181,23 +2171,48 @@ Dirección: %4</translation> <translation>Umbral de desconección de clientes con mal comportamiento (por defecto: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Aceptar comandos consola y JSON-RPC </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Correr como demonio y acepta comandos </translation> @@ -2209,7 +2224,7 @@ Dirección: %4</translation> </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2234,12 +2249,7 @@ Dirección: %4</translation> <translation>Precaución: Por favor revise que la fecha y hora de tu ordenador son correctas. Si tu reloj está mal configurado Bitcoin no funcionará correctamente.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2265,17 +2275,12 @@ Dirección: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Dirección -tor invalida: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2382,30 +2387,24 @@ Dirección: %4</translation> </translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Escucha conexiones JSON-RPC en el puerto <port> (predeterminado: 8332) -</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Permite conexiones JSON-RPC desde la dirección IP especificada </translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Envia comando al nodo situado en <ip> (predeterminado: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Actualizar billetera al formato actual</translation> </message> @@ -2432,12 +2431,12 @@ Dirección: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Usa OpenSSL (https) para las conexiones JSON-RPC </translation> @@ -2455,25 +2454,25 @@ Dirección: %4</translation> </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Cifrados aceptados (Predeterminado: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) </translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Este mensaje de ayuda </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>No se puede obtener permiso de trabajo en la carpeta de datos %s. Probablemente Bitcoin ya se está ejecutando. </translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2488,13 +2487,13 @@ Dirección: %4</translation> <translation>Conecta mediante proxy socks</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Permite búsqueda DNS para addnode y connect </translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Cargando direcciónes...</translation> </message> @@ -2524,12 +2523,12 @@ Dirección: %4</translation> <translation>Error cargando wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Dirección -proxy invalida: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2549,12 +2548,12 @@ Dirección: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Cantidad inválida para -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2564,12 +2563,12 @@ Dirección: %4</translation> <translation>Error: Billetera bloqueada, no es posible crear la transacción</translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Error: Esta transación requiere una comisión de al menos %s por su cantidad, complejidad o uso de fondos recibidos recientemente.</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Error: La transacción no se pudo crear </translation> </message> @@ -2579,12 +2578,12 @@ Dirección: %4</translation> <translation>Enviando...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Error: La transacción fue rechazada. Esto puede haber ocurrido si alguna de las monedas ya estaba gastada o si ha usado una copia de wallet.dat y las monedas se gastaron en la copia pero no se han marcado como gastadas aqui.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Cantidad inválida</translation> </message> @@ -2594,12 +2593,12 @@ Dirección: %4</translation> <translation>Fondos insuficientes</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Cargando el index de bloques...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Agrega un nodo para conectarse and attempt to keep the connection open</translation> </message> @@ -2609,7 +2608,7 @@ Dirección: %4</translation> <translation>No es posible escuchar en el %s en este ordenador. Probablemente Bitcoin ya se está ejecutando.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Buscar pares usando 'internet relay chat (IRC)' (predeterminado: 0)</translation> </message> @@ -2654,7 +2653,7 @@ Dirección: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2666,12 +2665,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Error</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_et.ts b/src/qt/locale/bitcoin_et.ts index 94113e3465..06420accaf 100644 --- a/src/qt/locale/bitcoin_et.ts +++ b/src/qt/locale/bitcoin_et.ts @@ -77,26 +77,26 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> <translation type="unfinished"/> </message> <message> - <location line="+3"/> - <source>&Verify Message</source> + <location line="-14"/> + <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> + <location line="+3"/> + <source>&Verify Message</source> <translation type="unfinished"/> </message> <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Kustuta</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation type="unfinished"/> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation type="unfinished"/> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation type="unfinished"/> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Ülevaade</translation> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Tehingud</translation> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation type="unfinished"/> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation type="unfinished"/> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation type="unfinished"/> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Valikud...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation type="unfinished"/> </message> @@ -398,42 +403,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Ekspordi...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation type="unfinished"/> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation type="unfinished"/> </message> @@ -448,7 +438,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -458,12 +448,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -473,7 +463,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation type="unfinished"/> </message> @@ -483,12 +473,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Fail</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Seaded</translation> </message> @@ -503,7 +493,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation type="unfinished"/> </message> @@ -520,7 +510,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation> </message> @@ -594,7 +584,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1101,7 +1091,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1252,7 +1242,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation type="unfinished"/> </message> <message> @@ -1282,7 +1272,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation type="unfinished"/> </message> <message> @@ -1638,7 +1628,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation type="unfinished"/> @@ -1749,12 +1739,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation type="unfinished"/> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>tundmatu</translation> </message> @@ -2065,7 +2055,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation type="unfinished"/> </message> @@ -2080,12 +2070,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation type="unfinished"/> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation type="unfinished"/> </message> @@ -2160,22 +2150,47 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation type="unfinished"/> </message> @@ -2185,7 +2200,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2210,12 +2225,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2240,17 +2250,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2355,27 +2360,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation type="unfinished"/> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation type="unfinished"/> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2400,12 +2400,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation type="unfinished"/> </message> @@ -2420,22 +2420,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation type="unfinished"/> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation type="unfinished"/> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation type="unfinished"/> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -2450,12 +2450,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation type="unfinished"/> </message> @@ -2485,12 +2485,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2510,12 +2510,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2525,12 +2525,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation type="unfinished"/> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation type="unfinished"/> </message> @@ -2540,12 +2540,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation type="unfinished"/> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation type="unfinished"/> </message> @@ -2555,12 +2555,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation type="unfinished"/> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation type="unfinished"/> </message> @@ -2570,7 +2570,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2615,7 +2615,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2627,12 +2627,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation type="unfinished"/> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_eu_ES.ts b/src/qt/locale/bitcoin_eu_ES.ts index 4e267c58bb..7ddbe35273 100644 --- a/src/qt/locale/bitcoin_eu_ES.ts +++ b/src/qt/locale/bitcoin_eu_ES.ts @@ -77,26 +77,26 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> <translation type="unfinished"/> </message> <message> - <location line="+3"/> - <source>&Verify Message</source> + <location line="-14"/> + <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> + <location line="+3"/> + <source>&Verify Message</source> <translation type="unfinished"/> </message> <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Ezabatu</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation type="unfinished"/> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation type="unfinished"/> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation type="unfinished"/> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation type="unfinished"/> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation type="unfinished"/> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation type="unfinished"/> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation type="unfinished"/> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation type="unfinished"/> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation type="unfinished"/> </message> @@ -398,42 +403,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation type="unfinished"/> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation type="unfinished"/> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation type="unfinished"/> </message> @@ -448,7 +438,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -458,12 +448,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -473,7 +463,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation type="unfinished"/> </message> @@ -483,12 +473,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation type="unfinished"/> </message> @@ -503,7 +493,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation type="unfinished"/> </message> @@ -520,7 +510,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation> </message> @@ -594,7 +584,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1101,7 +1091,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1252,7 +1242,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation type="unfinished"/> </message> <message> @@ -1282,7 +1272,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation type="unfinished"/> </message> <message> @@ -1638,7 +1628,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation type="unfinished"/> @@ -1749,12 +1739,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation type="unfinished"/> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation type="unfinished"/> </message> @@ -2065,7 +2055,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation type="unfinished"/> </message> @@ -2080,12 +2070,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation type="unfinished"/> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation type="unfinished"/> </message> @@ -2160,22 +2150,47 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation type="unfinished"/> </message> @@ -2185,7 +2200,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2210,12 +2225,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2240,17 +2250,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2355,27 +2360,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation type="unfinished"/> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation type="unfinished"/> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2400,12 +2400,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation type="unfinished"/> </message> @@ -2420,22 +2420,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation type="unfinished"/> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation type="unfinished"/> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation type="unfinished"/> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -2450,12 +2450,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation type="unfinished"/> </message> @@ -2485,12 +2485,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2510,12 +2510,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2525,12 +2525,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation type="unfinished"/> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation type="unfinished"/> </message> @@ -2540,12 +2540,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation type="unfinished"/> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation type="unfinished"/> </message> @@ -2555,12 +2555,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation type="unfinished"/> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation type="unfinished"/> </message> @@ -2570,7 +2570,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2615,7 +2615,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2627,12 +2627,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation type="unfinished"/> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_fa.ts b/src/qt/locale/bitcoin_fa.ts index d67871609a..0fa8895a76 100644 --- a/src/qt/locale/bitcoin_fa.ts +++ b/src/qt/locale/bitcoin_fa.ts @@ -77,11 +77,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&امضای پیام</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>یک پیام را برای حصول اطمینان از ورود به سیستم با آدرس bitcoin مشخص، شناسایی کنید</translation> </message> @@ -91,12 +96,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>شناسایی پیام</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>آدرس انتخاب شده از لیست حذف کنید. فقط آدرسهای ارسال شده می شود حذف کرد</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>حذف</translation> </message> @@ -228,24 +228,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>آیا اطمینان دارید که می خواهید wallet رمزگذاری شود؟</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>هشدار: Caps lock key روشن است</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>تغییر عبارت عبور</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Biticon هم اکنون بسته میشود تا فرایند رمزگذاری را تمام کند. به خاطر داشته باشید که رمزگذاری کیف پولتان نمیتواند به طور کامل بیتیکونهای شما را در برابر دزدیده شدن توسط بدافزارهایی که رایانه شما را آلوده میکنند، محافظت نماید.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -289,17 +294,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>امضا و پیام</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>همگام سازی با شبکه ...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>بررسی اجمالی</translation> </message> @@ -309,7 +314,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>نمای کلی پنجره نشان بده</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&معاملات</translation> </message> @@ -329,7 +334,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>ویرایش لیست آدرسها و بر چسب های ذخیره ای</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>در یافت سکه</translation> </message> @@ -339,12 +344,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>نمایش لیست آدرس ها برای در یافت پر داخت ها</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>رسال سکه ها</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>خروج</translation> </message> @@ -374,7 +379,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>تنظیمات...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>رمزگذاری wallet</translation> </message> @@ -400,42 +405,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>دانلود %1 از %2 بلاکهای تاریخچه تراکنش (%3% انجام شد)</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&;صادرات</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>سکه ها را به آدرس bitocin ارسال کن</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>پیام را برای اثبات آدرس Bitcoin خود امضا کنید</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>یک پیام را برای حصول اطمینان از ورود به سیستم با آدرس bitcoin مشخص، شناسایی کنید</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>امضا</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>انتخابهای پیکربندی را برای bitcoin اصلاح کن</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>داده ها نوارِ جاری را به فایل انتقال دهید</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>رمز بندی یا رمز گشایی پنجره</translation> </message> @@ -450,7 +440,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>عبارت عبور رمز گشایی پنجره تغییر کنید</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>اشکال زدایی از صفحه</translation> </message> @@ -460,12 +450,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>کنسول اشکال زدایی و تشخیص را باز کنید</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>بازبینی پیام</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>یت کویین </translation> </message> @@ -475,7 +465,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>wallet</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>در مورد bitcoin</translation> </message> @@ -485,12 +475,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&نمایش/ عدم نمایش</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>فایل</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>تنظیمات</translation> </message> @@ -505,7 +495,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>نوار ابزار زبانه ها</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>نوار ابزار عملیت</translation> </message> @@ -522,7 +512,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>مشتری Bitcoin</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>در صد ارتباطات فعال بیتکویین با شبکه %n</numerusform></translation> </message> @@ -599,7 +589,7 @@ Address: %4 آدرس %4</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>مدیریت URI</translation> @@ -1106,7 +1096,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>-</translation> </message> @@ -1257,8 +1247,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>اضافه کردن دریافت کننده</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1287,8 +1277,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&;ارسال</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1645,7 +1635,7 @@ Address: %4 <translation>تولید شده</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>فرستنده</translation> @@ -1757,12 +1747,12 @@ Address: %4 <translation>نادرست</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>هنوز با مو فقیت ارسال نشده</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>مشخص نیست </translation> </message> @@ -2074,7 +2064,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>سخه بیتکویین</translation> </message> @@ -2089,12 +2079,12 @@ Address: %4 <translation>ارسال فرمان به سرور یا باتکویین</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>لیست فومان ها</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>کمک برای فرمان </translation> </message> @@ -2169,22 +2159,47 @@ Address: %4 <translation>آستانه برای قطع ارتباط با همکاران بدرفتار (پیشفرض: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>مدت زمان به ثانیه برای جلوگیری از همکاران بدرفتار برای اتصال دوباره (پیشفرض: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation> تفکیک بلاک و آدرس بانک داده ها. افزایش زمان خاموشی (پیش فرض:0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>JSON-RPC قابل فرمانها و</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>اجرای در پس زمینه به عنوان شبح و قبول فرمان ها</translation> </message> @@ -2194,7 +2209,7 @@ Address: %4 <translation>استفاده شبکه آزمایش</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>پذیرش اتصالات از بیرون (پیش فرض:1 بدون پراکسی یا اتصال)</translation> </message> @@ -2219,12 +2234,7 @@ Address: %4 <translation>هشدار: لطفا زمان و تاریخ رایانه خود را تصحیح نمایید! اگر ساعت رایانه شما اشتباه باشد bitcoin ممکن است صحیح کار نکند</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>در زمان تنظیم درگاه RPX %i در فهرست کردن %s اشکالی رخ داده است</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>بستن گزینه ایجاد</translation> </message> @@ -2249,17 +2259,12 @@ Address: %4 <translation>قرینه ها را برای جستجوی DNS بیاب (پیش فرض: 1 مگر در زمان اتصال)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>وارد کردن بلوکها</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>آدرس نرم افزار تور غلط است %s</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>حداکثر بافر دریافت شده بر اساس اتصال <n>* 1000 بایت (پیش فرض:5000)</translation> </message> @@ -2364,27 +2369,22 @@ Address: %4 <translation>JSON-RPC عبارت عبور برای ارتباطات</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>( 8332پیش فرض :) &lt;poort&gt; JSON-RPC شنوایی برای ارتباطات</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>از آدرس آی پی خاص JSON-RPC قبول ارتباطات</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>(127.0.0.1پیش فرض: ) &lt;ip&gt; دادن فرمانها برای استفاده گره ها روی</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>زمانی که بهترین بلاک تغییر کرد، دستور را اجرا کن (%s در cmd با block hash جایگزین شده است)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>wallet را به جدیدترین فرمت روزآمد کنید</translation> </message> @@ -2409,12 +2409,12 @@ Address: %4 <translation>چقد کامل بلوک تصدیق است (0-6, پیش فرض:1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>صدور بلاکها از فایل خارجی blk000?.dat </translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>JSON-RPCبرای ارتباطات استفاده کنید OpenSSL (https)</translation> </message> @@ -2429,22 +2429,22 @@ Address: %4 <translation>(server.pemپیش فرض: ) کلید خصوصی سرور</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>رمز های قابل قبول( TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>پیام کمکی</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>رمز گشایی دایرکتور داده ها امکان پذیر نیست. شاید بیت کویین در حال فعال می باشد%s</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>یت کویین </translation> </message> @@ -2459,12 +2459,12 @@ Address: %4 <translation>اتصال از طریق پراکسی ساکس</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>به DNS اجازه بده تا برای addnode ، seednode و اتصال جستجو کند</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>بار گیری آدرس ها</translation> </message> @@ -2494,12 +2494,12 @@ Address: %4 <translation>خطا در بارگیری wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>آدرس پراکسی اشتباه %s</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>شبکه مشخص شده غیرقابل شناسایی در onlynet: '%s'</translation> </message> @@ -2519,12 +2519,12 @@ Address: %4 <translation>آدرس خارجی قابل اتصال- شناسایی نیست %s</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>میزان وجه اشتباه برای paytxfee=<میزان وجه>: %s</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>خطا: امکان شروع گره وجود ندارد</translation> </message> @@ -2534,12 +2534,12 @@ Address: %4 <translation>خطا: Wallet قفل شده است. ایجاد تراکنش امکان پذیر نیست</translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>خطا: این تراکنش نیازمند هزینه تراکنش به مبلغ حداقل %s است به علت میزان وجه، دشواری، یا استفاده از وجوه دریافتی اخیر</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>خطا ایجاد معامله اشتباه است</translation> </message> @@ -2549,12 +2549,12 @@ Address: %4 <translation>ارسال...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>خطا . معامله رد شد.این هنگامی که سکه ها در والت شما هنوز ارسال شده اند ولی شما کپی والت استفاده می کنید و سکه ها روی کپی فرستاده شده اند و به عنوان ارسال شنه مشخص نشده اتفاقی می افتد.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>میزان وجه اشتباه</translation> </message> @@ -2564,12 +2564,12 @@ Address: %4 <translation>بود جه نا کافی </translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>بار گیری شاخص بلوک</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>به اتصال یک گره اضافه کنید و اتصال را باز نگاه دارید</translation> </message> @@ -2579,7 +2579,7 @@ Address: %4 <translation>اتصال به %s از این رایانه امکان پذیر نیست. Bitcoin احتمالا در حال اجراست.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>یافتنت قرینه با استفاده از internet relay chat (پیش فرض:0)</translation> </message> @@ -2624,7 +2624,7 @@ Address: %4 <translation>برای استفاده از %s از انتخابات</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2637,12 +2637,12 @@ If the file does not exist, create it with owner-readable-only file permissions. </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>خطا</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_fa_IR.ts b/src/qt/locale/bitcoin_fa_IR.ts index f9d5ab8590..81ae9f471f 100644 --- a/src/qt/locale/bitcoin_fa_IR.ts +++ b/src/qt/locale/bitcoin_fa_IR.ts @@ -77,11 +77,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>و امضای پیام </translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -91,12 +96,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>آدرس انتخاب شده را از لیست حذف کنید. تنها آدرس ارسال شده می تواند حذف شود</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>و حذف</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>تایید رمزگذاری</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin برای اتمام فرایند رمزگذاری بسته خواهد شد. به خاطر داشته باشید که رمزگذاری WALLET شما، کامپیوتر شما را از آلودگی به بدافزارها مصون نمی دارد.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>امضا و پیام</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>به روز رسانی با شبکه...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>و بازبینی</translation> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>نمای کلی از wallet را نشان بده</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>و تراکنش</translation> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>فهرست آدرسها و برچسبهای ذخیره شده را ویرایش کن</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>و دریافت سکه ها</translation> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>فهرست آدرسها را برای دریافت وجه نشان بده</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>و ارسال سکه ها</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>خروج</translation> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>و انتخابها</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>و رمزگذاری wallet</translation> </message> @@ -399,42 +404,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>دانلود %1 از %2 بلاک مربوط به تاریخچه تراکنش (%3% انجام شده است)</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>و صدور</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>اصلاح انتخابها برای پیکربندی Bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>صدور داده نوار جاری به یک فایل</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>رمزگذاری با رمزگشایی از wallet</translation> </message> @@ -449,7 +439,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>رمز مربوط به رمزگذاریِ wallet را تغییر دهید</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -459,12 +449,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>bitcoin</translation> </message> @@ -474,7 +464,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>کیف پول</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&در مورد بیتکویین</translation> </message> @@ -484,12 +474,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&نمایش/ عدم نمایش و</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>و فایل</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>و تنظیمات</translation> </message> @@ -504,7 +494,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>نوار ابزار</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>نوار عملیات</translation> </message> @@ -521,7 +511,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>مشتری bitcoin</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n ارتباط فعال به شبکه Bitcoin %n ارتباط فعال به شبکه Bitcoin</numerusform></translation> @@ -602,7 +592,7 @@ Address: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1111,7 +1101,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1262,7 +1252,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation type="unfinished"/> </message> <message> @@ -1292,8 +1282,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>و ارسال</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1648,7 +1638,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation type="unfinished"/> @@ -1759,12 +1749,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>تا به حال با موفقیت انتشار نیافته است</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>ناشناس</translation> </message> @@ -2077,7 +2067,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>نسخه bitcoin</translation> </message> @@ -2092,12 +2082,12 @@ Address: %4 <translation>ارسال دستور به سرور یا bitcoined</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>فهرست دستورها</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>درخواست کمک برای یک دستور</translation> </message> @@ -2172,22 +2162,47 @@ Address: %4 <translation>آستانه قطع برای قرینه سازی اشتباه (پیش فرض:100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>تعداد ثانیه ها برای اتصال دوباره قرینه های اشتباه (پیش فرض:86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>command line و JSON-RPC commands را قبول کنید</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>به عنوان daemon بک گراند را اجرا کنید و دستورات را قبول نمایید</translation> </message> @@ -2197,7 +2212,7 @@ Address: %4 <translation>از تستِ شبکه استفاده نمایید</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2222,12 +2237,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2252,17 +2262,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2367,27 +2372,22 @@ Address: %4 <translation>رمز برای ارتباطاتِ JSON-RPC</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>ارتباطاتِ JSON-RPC را در <port> گوش کنید (پیش فرض:8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>ارتباطاتِ JSON-RPC را از آدرس آی.پی. مشخصی برقرار کنید.</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>دستورات را به گره اجرا شده در<ip> ارسال کنید (پیش فرض:127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>دستور را وقتی بهترین بلاک تغییر کرد اجرا کن (%s در دستور توسط block hash جایگزین شده است)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>wallet را به جدیدترین نسخه روزآمد کنید</translation> </message> @@ -2412,12 +2412,12 @@ Address: %4 <translation>چگونگی تایید تمامی بلاکها (پیش فرض: 1 و 0-6)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>برای ارتباطاتِ JSON-RPC از OpenSSL (https) استفاده کنید</translation> </message> @@ -2432,22 +2432,22 @@ Address: %4 <translation>رمز اختصاصی سرور (پیش فرض: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>ciphers قابل قبول (پیش فرض: default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>این پیام راهنما</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>قفل دایرکتوری داده ها %s قابل دریافت نیست. احتمال این وجود دارد که Bitcoin در حال اجرا باشد</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>bitcoin</translation> </message> @@ -2462,12 +2462,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>لود شدن آدرسها..</translation> </message> @@ -2497,12 +2497,12 @@ Address: %4 <translation>خطا در هنگام لود شدن wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2522,12 +2522,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>میزان اشتباه است for -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2537,12 +2537,12 @@ Address: %4 <translation>خطا: wallet قفل شده است، ایجاد تراکنش امکان پذیر نیست</translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>خطا: تراکنش نیازمند پرداخت هزینه به میران حداقل %s است به علت هزینه، دشواری عملیات یا استفاده از وجوه دریافت شده اخیر</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>خطا: ایجاد تراکنش امکان پذیر نیست</translation> </message> @@ -2552,12 +2552,12 @@ Address: %4 <translation>در حال ارسال...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>خطا: تراکنش تایید نشد. این خطا ممکن است به این دلیل اتفاق بیافتد که سکه های wallet شما خرج شده باشند مثلا اگر wallet.dat را مپی کرده باشید و سکه های شما در آن کپی استفاده شده باشند اما در اینجا نمایش داده نشده اند.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>میزان اشتباه است</translation> </message> @@ -2567,12 +2567,12 @@ Address: %4 <translation>وجوه ناکافی</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>لود شدن نمایه بلاکها..</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>یک گره برای اتصال اضافه کنید و تلاش کنید تا اتصال را باز نگاه دارید</translation> </message> @@ -2582,7 +2582,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>یافتن همتا/دوست با استفاده از internet relay chat (پیش فرض:0)</translation> </message> @@ -2627,7 +2627,7 @@ Address: %4 <translation>برای استفاده از %s از اختیارات</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2640,12 +2640,12 @@ If the file does not exist, create it with owner-readable-only file permissions. </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>خطا</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_fi.ts b/src/qt/locale/bitcoin_fi.ts index 8b69f1b69c..3380833dab 100644 --- a/src/qt/locale/bitcoin_fi.ts +++ b/src/qt/locale/bitcoin_fi.ts @@ -83,11 +83,16 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Allekirjoita viesti</translation> + <source>Sign &Message</source> + <translation>Allekirjoita &viesti</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Tarkista viestin allekirjoitus varmistaaksesi, että se allekirjoitettiin tietyllä Bitcoin-osoitteella</translation> </message> @@ -97,12 +102,7 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>&Varmista viesti...</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Poista valittuna oleva osoite listasta. Vain lähettämiseen käytettäviä osoitteita voi poistaa.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Poista</translation> </message> @@ -233,24 +233,29 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Haluatko varmasti salata lompakkosi?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Varoitus: Caps Lock on aktiivinen!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Lompakko salattu</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin sulkeutuu lopettaakseen salausprosessin. Muista, että salattu lompakko ei täysin suojaa sitä haittaohjelmien aiheuttamilta varkauksilta.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -294,17 +299,17 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>&Allekirjoita viesti...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synkronoidaan verkon kanssa...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Yleisnäkymä</translation> </message> @@ -314,7 +319,7 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Näyttää kokonaiskatsauksen lompakon tilanteesta</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Rahansiirrot</translation> </message> @@ -334,7 +339,7 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Muokkaa tallennettujen nimien ja osoitteiden listaa</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Vastaanota Bitcoineja</translation> </message> @@ -344,12 +349,12 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Näytä Bitcoinien vastaanottamiseen käytetyt osoitteet</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Lähetä Bitcoineja</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>L&opeta</translation> </message> @@ -379,7 +384,7 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>&Asetukset...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Salaa lompakko...</translation> </message> @@ -404,42 +409,27 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Ladattu %1 / %2 lohkoista rahansiirtohistoriasta (%3% suoritettu).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Vie...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Lähetä kolikoita Bitcoin-osoitteeseen</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Allekirjoita viesti todistaaksesi, että omistat Bitcoin-osoitteen</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Tarkista viestin allekirjoitus varmistaaksesi, että se allekirjoitettiin tietyllä Bitcoin-osoitteella</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>Allek&irjoitukset</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Muuta Bitcoinin konfiguraatioasetuksia</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Vie auki olevan välilehden tiedot tiedostoon</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Salaa tai poista salaus lompakosta</translation> </message> @@ -454,7 +444,7 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Vaihda lompakon salaukseen käytettävä tunnuslause</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Debug ikkuna</translation> </message> @@ -464,12 +454,12 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Avaa debuggaus- ja diagnostiikkakonsoli</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>Varmista &viesti...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -479,7 +469,7 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Lompakko</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>Tieto&a Bitcoinista</translation> </message> @@ -489,12 +479,12 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>&Näytä / Piilota</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Tiedosto</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Asetukset</translation> </message> @@ -509,7 +499,7 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Välilehtipalkki</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Toimintopalkki</translation> </message> @@ -526,7 +516,7 @@ Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.o <translation>Bitcoin-asiakas</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktiivinen yhteys Bitcoin-verkkoon</numerusform><numerusform>%n aktiivista yhteyttä Bitcoin-verkkoon</numerusform></translation> </message> @@ -603,7 +593,7 @@ Tyyppi: %3 Osoite: %4</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>URI käsittely</translation> @@ -1110,7 +1100,7 @@ Osoite: %4</translation> <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>Ei saatavilla</translation> </message> @@ -1261,8 +1251,8 @@ Osoite: %4</translation> </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Lisää Vastaanottaja</translation> + <source>Add &Recipient</source> + <translation>Lisää &Vastaanottaja</translation> </message> <message> <location line="+20"/> @@ -1291,8 +1281,8 @@ Osoite: %4</translation> </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Lähetä</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1647,7 +1637,7 @@ Osoite: %4</translation> <translation>Generoitu</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Lähettäjä</translation> @@ -1758,12 +1748,12 @@ Osoite: %4</translation> <translation>epätosi</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, ei ole vielä onnistuneesti lähetetty</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>tuntematon</translation> </message> @@ -2074,7 +2064,7 @@ Osoite: %4</translation> <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoinin versio</translation> </message> @@ -2089,12 +2079,12 @@ Osoite: %4</translation> <translation>Lähetä käsky palvelimelle tai bitcoind:lle</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Lista komennoista</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Hanki apua käskyyn</translation> </message> @@ -2169,22 +2159,47 @@ Osoite: %4</translation> <translation>Kynnysarvo aikakatkaisulle heikosti toimiville verkoille (oletus: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Sekuntien määrä, kuinka kauan uudelleenkytkeydytään verkkoihin (oletus: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Irroita lohko ja osoite-tietokannat. Pidentää sammutus-aikaa (vakioasetus: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Hyväksy merkkipohjaiset- ja JSON-RPC-käskyt</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Aja taustalla daemonina ja hyväksy komennot</translation> </message> @@ -2194,7 +2209,7 @@ Osoite: %4</translation> <translation>Käytä test -verkkoa</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Hyväksy yhteyksiä ulkopuolelta (vakioasetus: 1 jos -proxy tai -connect ei määritelty)</translation> </message> @@ -2219,12 +2234,7 @@ Osoite: %4</translation> <translation>Varoitus: Tarkista että tietokoneesi kellonaika ja päivämäärä ovat paikkansapitäviä! Bitcoin ei toimi oikein väärällä päivämäärällä ja/tai kellonajalla.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Virhe valmisteltaessa RPC-portin %i avaamista kuunneltavaksi: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Lohkon luonnin asetukset:</translation> </message> @@ -2249,17 +2259,12 @@ Osoite: %4</translation> <translation>Hae naapureita DNS hauilla (vakioasetus: 1 paitsi jos -connect)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Tuodaan lohkoja...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Virheellinen -tor osoite '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Suurin vastaanottopuskuri yksittäiselle yhteydelle, <n>*1000 tavua (vakioasetus: 5000)</translation> </message> @@ -2364,27 +2369,22 @@ Osoite: %4</translation> <translation>Salasana JSON-RPC-yhteyksille</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Kuuntele JSON-RPC -yhteyksiä portista <port> (oletus: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Salli JSON-RPC yhteydet tietystä ip-osoitteesta</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Lähetä käskyjä solmuun osoitteessa <ip> (oletus: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Suorita käsky kun paras lohko muuttuu (%s cmd on vaihdettu block hashin kanssa)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Päivitä lompakko uusimpaan formaattiin</translation> </message> @@ -2409,12 +2409,12 @@ Osoite: %4</translation> <translation>Kuinka tiukka lohkovarmistus on (0-6, oletus: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Tuo lohkoja ulkoisesta blk000?.dat tiedostosta</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Käytä OpenSSL:ää (https) JSON-RPC-yhteyksille</translation> </message> @@ -2429,23 +2429,23 @@ Osoite: %4</translation> <translation>Palvelimen yksityisavain (oletus: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Hyväksyttävä salaus (oletus: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Tämä ohjeviesti</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>En pääse käsiksi data-hakemiston lukitukseen %s. Bitcoin on todennäköisesti jo käynnistetty.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2460,12 +2460,12 @@ TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> <translation>Yhdistä socks proxyn läpi</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Salli DNS kyselyt -addnode, -seednode ja -connect yhteydessä</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Ladataan osoitteita...</translation> </message> @@ -2495,12 +2495,12 @@ TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> <translation>Virhe ladattaessa wallet.dat-tiedostoa</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Virheellinen proxy-osoite '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Tuntematon verkko -onlynet parametrina: '%s'</translation> </message> @@ -2520,12 +2520,12 @@ TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> <translation>-externalip osoitteen '%s' selvittäminen epäonnistui</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>-paytxfee=<amount>: '%s' on virheellinen</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Virhe: ei pystytty käynnistämään noodia.</translation> </message> @@ -2535,12 +2535,12 @@ TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> <translation>Virhe: Lompakko on lukittu, rahansiirtoa ei voida luoda</translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Virhe: Tämä rahansiirto vaatii rahansiirtopalkkion vähintään %s johtuen sen määrästä, monimutkaisuudesta tai hiljattain vastaanotettujen summien käytöstä</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Virhe: Rahansiirron luonti epäonnistui</translation> </message> @@ -2550,12 +2550,12 @@ TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> <translation>Lähetetään...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Virhe: Rahansiirto hylättiin. Tämä voi tapahtua jos jotkin bitcoineistasi on jo käytetty, esimerkiksi jos olet käyttänyt kopiota wallet.dat-lompakkotiedostosta ja bitcoinit on merkitty käytetyksi vain kopiossa.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Virheellinen määrä</translation> </message> @@ -2565,12 +2565,12 @@ TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> <translation>Lompakon saldo ei riitä</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Ladataan lohkoindeksiä...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Linää solmu mihin liittyä pitääksesi yhteyden auki</translation> </message> @@ -2580,7 +2580,7 @@ TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> <translation>Kytkeytyminen %s ei onnistu tällä tietokoneella. Bitcoin on todennäköisesti jo ajamassa.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Etsi solmuja käyttäen internet relay chatia (oletus: 0)</translation> </message> @@ -2625,7 +2625,7 @@ TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> <translation>Käytä %s optiota</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2644,12 +2644,12 @@ Jos tiedostoa ei ole, niin luo se ainoastaan omistajan kirjoitusoikeuksin. </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Virhe</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts index 3937e4f205..77c4279dba 100644 --- a/src/qt/locale/bitcoin_fr.ts +++ b/src/qt/locale/bitcoin_fr.ts @@ -82,11 +82,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Signer un message</translation> + <source>Sign &Message</source> + <translation>Signer un &message</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>Effacer l'adresse actuellement sélectionnée de la liste</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Vérifier un message pour vous assurer qu'il a bien été signé avec l'adresse Bitcoin spécifiée</translation> </message> @@ -96,12 +101,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Vérifier un message</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Supprimer l'adresse sélectionnée dans la liste. Seules les adresses d'envoi peuvent être supprimées.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Supprimer</translation> </message> @@ -232,24 +232,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Êtes-vous sûr de vouloir chiffrer votre porte-monnaie ?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>IMPORTANT : Les sauvegardes précédentes de votre fichier de porte-monnaie devraient être remplacées par le nouveau fichier crypté de porte-monnaie. Pour des raisons de sécurité, les précédentes sauvegardes de votre fichier de porte-monnaie non chiffré deviendront inutilisables dès que vous commencerez à utiliser le nouveau porte-monnaie chiffré.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Attention : la touche Verr. Maj. est activée !</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Porte-monnaie chiffré</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin va à présent se fermer pour terminer la procédure de cryptage. N'oubliez pas que le chiffrement de votre porte-monnaie ne peut pas fournir une protection totale contre le vol par des logiciels malveillants qui infecteraient votre ordinateur.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Signer un &message...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synchronisation avec le réseau…</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Vue d'ensemble</translation> </message> @@ -313,7 +318,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Afficher une vue d’ensemble du porte-monnaie</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transactions</translation> </message> @@ -333,7 +338,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Éditer la liste des adresses et des étiquettes stockées</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Recevoir des pièces</translation> </message> @@ -343,12 +348,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Afficher la liste des adresses pour recevoir des paiements</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Envoyer des pièces</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>Q&uitter</translation> </message> @@ -378,7 +383,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Options…</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Chiffrer le porte-monnaie...</translation> </message> @@ -403,42 +408,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>%1 blocs de l'historique des transactions sur %2 téléchargés (%3% effectué).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exporter…</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Envoyer des pièces à une adresse Bitcoin</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Signer un message pour prouver que vous détenez une adresse Bitcoin</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Vérifier un message pour vous assurer qu'il a bien été signé avec l'adresse Bitcoin spécifiée</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>S&ignatures</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Modifier les options de configuration de Bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Exporter les données de l'onglet courant vers un fichier</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Chiffrer ou déchiffrer le porte-monnaie</translation> </message> @@ -453,7 +443,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Modifier la phrase de passe utilisée pour le chiffrement du porte-monnaie</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>Fenêtre de &débogage</translation> </message> @@ -463,12 +453,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Ouvrir une console de débogage et de diagnostic</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Vérifier un message...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,7 +468,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Porte-monnaie</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>À &propos de Bitcoin</translation> </message> @@ -488,12 +478,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Afficher / Cacher</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Fichier</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Réglages</translation> </message> @@ -508,7 +498,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Barre d'outils des onglets</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Barre d'outils des actions</translation> </message> @@ -525,7 +515,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Client Bitcoin</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n connexion active avec le réseau Bitcoin</numerusform><numerusform>%n connexions actives avec le réseau Bitcoin</numerusform></translation> </message> @@ -603,7 +593,7 @@ Adresse : %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>Gestion des URI</translation> @@ -1110,7 +1100,7 @@ Adresse : %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>Indisponible</translation> </message> @@ -1261,8 +1251,8 @@ Adresse : %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Ajouter un destinataire</translation> + <source>Add &Recipient</source> + <translation>Ajouter un &destinataire</translation> </message> <message> <location line="+20"/> @@ -1291,8 +1281,8 @@ Adresse : %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Envoyer</translation> + <source>S&end</source> + <translation>E&nvoyer</translation> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1647,7 +1637,7 @@ Adresse : %4 <translation>Génération</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>De</translation> @@ -1758,12 +1748,12 @@ Adresse : %4 <translation>faux</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, n’a pas encore été diffusée avec succès</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>inconnu</translation> </message> @@ -2074,7 +2064,7 @@ Adresse : %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Version de Bitcoin</translation> </message> @@ -2089,12 +2079,12 @@ Adresse : %4 <translation>Envoyer une commande à -server ou à bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Lister les commandes</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Obtenir de l’aide pour une commande</translation> </message> @@ -2169,22 +2159,47 @@ Adresse : %4 <translation>Seuil de déconnexion des pairs de mauvaise qualité (par défaut : 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Délai en secondes de refus de reconnexion aux pairs de mauvaise qualité (par défaut : 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>Une erreur est survenue lors de la mise en place du port RPC %i pour écouter sur IPv6, retour à IPv4 : %s</translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>Une erreur est survenue lors de la mise en place du port RPC %u pour écouter sur IPv4 : %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Détacher les bases de données des blocs et des adresses. Augmente le délai de fermeture (par défaut : 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>Écouter les connexions JSON-RPC sur le <port> (par défaut : 8332 ou tesnet : 18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accepter les commandes de JSON-RPC et de la ligne de commande</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>Importation du fichier de données de la chaîne des blocs.</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>Importation du fichier de données de démarrage de la chaîne des blocs.</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Fonctionner en arrière-plan en tant que démon et accepter les commandes</translation> </message> @@ -2194,7 +2209,7 @@ Adresse : %4 <translation>Utiliser le réseau de test</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accepter les connexions entrantes (par défaut : 1 si -proxy ou -connect ne sont pas présents)</translation> </message> @@ -2219,12 +2234,7 @@ Adresse : %4 <translation>Attention : veuillez vérifier que l'heure et la date de votre ordinateur sont correctes ! Si votre horloge n'est pas à l'heure, Bitcoin ne fonctionnera pas correctement.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Une erreur est survenue lors de la mise en place du port d'écoute RPC %i : %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Options de création des blocs :</translation> </message> @@ -2249,17 +2259,12 @@ Adresse : %4 <translation>Trouver des pairs en utilisant la recherche DNS (par défaut : 1 sauf si -connect est utilisé)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Importation des blocs…</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Adresse -tor invalide : « %s »</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Tampon maximal de réception par -connection, <n>*1000 octets (par défaut : 5000)</translation> </message> @@ -2364,27 +2369,22 @@ Adresse : %4 <translation>Mot de passe pour les connexions JSON-RPC</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Écouter les connexions JSON-RPC sur le <port> (par défaut : 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Autoriser les connexions JSON-RPC depuis l'adresse IP spécifiée</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Envoyer des commandes au nœud fonctionnant à <ip> (par défaut : 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Exécuter la commande lorsque le meilleur bloc change (%s est remplacé par le hachage du bloc dans cmd)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Mettre à jour le format du porte-monnaie</translation> </message> @@ -2409,12 +2409,12 @@ Adresse : %4 <translation>Profondeur de la vérification des blocs (0-6, par défaut : 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Importe des blocs depuis un fichier blk000?.dat externe</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Utiliser OpenSSL (https) pour les connexions JSON-RPC</translation> </message> @@ -2429,22 +2429,22 @@ Adresse : %4 <translation>Clef privée du serveur (par défaut : server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Clefs de chiffrement acceptables (par défaut : TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Ce message d'aide</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Impossible d’obtenir un verrou sur le répertoire de données %s. Le programme Bitcoin est probablement déjà lancé.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2459,12 +2459,12 @@ Adresse : %4 <translation>Connexion via un proxy socks</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Autoriser les recherches DNS pour -addnode, -seednode et -connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Chargement des adresses…</translation> </message> @@ -2494,12 +2494,12 @@ Adresse : %4 <translation>Erreur lors du chargement de wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Adresse -proxy invalide : « %s »</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Réseau inconnu spécifié sur -onlynet : « %s »</translation> </message> @@ -2519,12 +2519,12 @@ Adresse : %4 <translation>Impossible de résoudre l'adresse -externalip : « %s »</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Montant invalide pour -paytxfee=<montant> : « %s »</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Erreur : le nœud n'a pu être démarré</translation> </message> @@ -2534,12 +2534,12 @@ Adresse : %4 <translation>Erreur : le porte-monnaie est verrouillé, impossible de créer la transaction </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Erreur : cette transaction nécessite des frais de transaction d'au moins %s en raison de son montant, de sa complexité ou parce que des fonds reçus récemment sont utilisés </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Erreur : échec de la création de la transaction </translation> </message> @@ -2549,12 +2549,12 @@ Adresse : %4 <translation>Envoi en cours...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Erreur : la transaction a été rejetée. Cela peut arriver si certaines pièces de votre porte-monnaie ont déjà été dépensées, par exemple si vous avez utilisé une copie de wallet.dat et si des pièces ont été dépensées avec cette copie sans être marquées comme telles ici.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Montant invalide</translation> </message> @@ -2564,12 +2564,12 @@ Adresse : %4 <translation>Fonds insuffisants</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Chargement de l’index des blocs…</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Ajouter un nœud auquel se connecter et tenter de garder la connexion ouverte</translation> </message> @@ -2579,7 +2579,7 @@ Adresse : %4 <translation>Impossible de se lier à %s sur cet ordinateur. Bitcoin fonctionne probablement déjà.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Trouver des pairs en utilisant Internet Relay Chat (par défaut : 0)</translation> </message> @@ -2624,7 +2624,7 @@ Adresse : %4 <translation>Pour utiliser l'option %s</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2643,12 +2643,12 @@ Si le fichier n'existe pas, créez-le avec les droits de lecture accordés </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Erreur</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_fr_CA.ts b/src/qt/locale/bitcoin_fr_CA.ts index d0e3378fcc..16861ed570 100644 --- a/src/qt/locale/bitcoin_fr_CA.ts +++ b/src/qt/locale/bitcoin_fr_CA.ts @@ -82,11 +82,16 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -96,12 +101,7 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Supprimer l'adresse sélectionnée dans la liste. Seules les adresses d'envoi peuvent être supprimé.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Supprimer</translation> </message> @@ -232,24 +232,29 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation type="unfinished"/> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation type="unfinished"/> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation type="unfinished"/> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation type="unfinished"/> </message> @@ -313,7 +318,7 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation type="unfinished"/> </message> @@ -333,7 +338,7 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation type="unfinished"/> </message> @@ -343,12 +348,12 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation type="unfinished"/> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation type="unfinished"/> </message> @@ -378,7 +383,7 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation type="unfinished"/> </message> @@ -403,42 +408,27 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation type="unfinished"/> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation type="unfinished"/> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation type="unfinished"/> </message> @@ -453,7 +443,7 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -463,12 +453,12 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -478,7 +468,7 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation type="unfinished"/> </message> @@ -488,12 +478,12 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation type="unfinished"/> </message> @@ -508,7 +498,7 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation type="unfinished"/> </message> @@ -525,7 +515,7 @@ Ce produit comprend des logiciels développés par le projet OpenSSL pour être <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation> </message> @@ -599,7 +589,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1106,7 +1096,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1257,7 +1247,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation type="unfinished"/> </message> <message> @@ -1287,7 +1277,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation type="unfinished"/> </message> <message> @@ -1643,7 +1633,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation type="unfinished"/> @@ -1754,12 +1744,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation type="unfinished"/> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation type="unfinished"/> </message> @@ -2070,7 +2060,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation type="unfinished"/> </message> @@ -2085,12 +2075,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation type="unfinished"/> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation type="unfinished"/> </message> @@ -2165,22 +2155,47 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation type="unfinished"/> </message> @@ -2190,7 +2205,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2215,12 +2230,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2245,17 +2255,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2360,27 +2365,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation type="unfinished"/> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation type="unfinished"/> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2405,12 +2405,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation type="unfinished"/> </message> @@ -2425,22 +2425,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation type="unfinished"/> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation type="unfinished"/> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation type="unfinished"/> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -2455,12 +2455,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation type="unfinished"/> </message> @@ -2490,12 +2490,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2515,12 +2515,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2530,12 +2530,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation type="unfinished"/> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation type="unfinished"/> </message> @@ -2545,12 +2545,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation type="unfinished"/> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation type="unfinished"/> </message> @@ -2560,12 +2560,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation type="unfinished"/> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation type="unfinished"/> </message> @@ -2575,7 +2575,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2620,7 +2620,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2632,12 +2632,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation type="unfinished"/> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_he.ts b/src/qt/locale/bitcoin_he.ts index f4bee56346..a85e86fe04 100644 --- a/src/qt/locale/bitcoin_he.ts +++ b/src/qt/locale/bitcoin_he.ts @@ -82,11 +82,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>חתום על הו&דעה</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>אמת הודעה בכדי להבטיח שהיא נחתמה עם כתובת ביטקוין מסוימת.</translation> </message> @@ -96,12 +101,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>אמת הודעה</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>מחק את הכתובת המסומנת מהרשימה. ניתן למחוק רק כתובות לשליחה.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&מחיקה</translation> </message> @@ -232,24 +232,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>האם אתה בטוח שברצונך להצפין את הארנק?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>זהירות: מקש Caps Lock מופעל!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>הארנק הוצפן</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>ביטקוין ייסגר עכשיו כדי להשלים את תהליך ההצפנה. זכור שהצפנת הארנק שלך אינו יכול להגן באופן מלא על הביטקוינים שלך מתוכנות זדוניות המושתלות על המחשב.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>חתום על הודעה</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>מסתנכרן עם הרשת...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&סקירה</translation> </message> @@ -313,7 +318,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>הצג סקירה כללית של הארנק</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&פעולות</translation> </message> @@ -333,7 +338,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>ערוך את רשימת הכתובות והתויות</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&קבלת מטבעות</translation> </message> @@ -343,12 +348,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>הצג את רשימת הכתובות לקבלת תשלומים</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&שלח מטבעות</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>י&ציאה</translation> </message> @@ -378,7 +383,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&אפשרויות</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>הצפן ארנק</translation> </message> @@ -403,42 +408,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>הורדו %1 בלוקים של היסטוריית פעולות מתוך %2 (הושלם %3% מהסה"כ).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>י&צא לקובץ</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>שלח מטבעות לכתובת ביטקוין</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>חתום על הודעה בכדי להוכיח כי אתה הבעלים של כתובת ביטקוין.</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>אמת הודעה בכדי להבטיח שהיא נחתמה עם כתובת ביטקוין מסוימת.</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>חתימות</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>שנה אפשרויות תצורה עבור ביטקוין</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>יצוא הנתונים בטאב הנוכחי לקובץ</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>הצפן או פענח ארנק</translation> </message> @@ -453,7 +443,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>שנה את הסיסמה להצפנת הארנק</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>חלון ניפוי</translation> </message> @@ -463,12 +453,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>פתח את לוח הבקרה לאבחון וניפוי</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>אמת הודעה...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>ביטקוין</translation> </message> @@ -478,7 +468,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>ארנק</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>אודות ביטקוין</translation> </message> @@ -488,12 +478,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>הצג / הסתר</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&קובץ</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>ה&גדרות</translation> </message> @@ -508,7 +498,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>סרגל כלים טאבים</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>סרגל כלים פעולות</translation> </message> @@ -525,7 +515,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>תוכנת ביטקוין</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>חיבור פעיל אחד לרשת הביטקוין</numerusform><numerusform>%n חיבורים פעילים לרשת הביטקוין</numerusform></translation> </message> @@ -602,7 +592,7 @@ Address: %4 כתובת: %4</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>תפעול URI</translation> @@ -1109,7 +1099,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>N/A</translation> </message> @@ -1260,8 +1250,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>הוסף מקבל</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1290,8 +1280,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&שלח</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1646,7 +1636,7 @@ Address: %4 <translation>נוצר</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>מאת</translation> @@ -1757,12 +1747,12 @@ Address: %4 <translation>שקר</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, טרם שודר בהצלחה</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>לא ידוע</translation> </message> @@ -2073,7 +2063,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>גרסת ביטקוין</translation> </message> @@ -2088,12 +2078,12 @@ Address: %4 <translation>שלח פקודה ל -server או bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>רשימת פקודות</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>קבל עזרה עבור פקודה</translation> </message> @@ -2168,22 +2158,47 @@ Address: %4 <translation>סף להתנתקות מעמיתים הנוהגים שלא כהלכה (ברירת מחדל: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>מספר שניות למנוע מעמיתים הנוהגים שלא כהלכה מלהתחבר מחדש (ברירת מחדל: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>נתק מסדי נתונים של בלוקים וכתובות. מגדיל את זמן הסגירה (ברירת מחדל: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>קבל פקודות משורת הפקודה ו- JSON-RPC</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>רוץ ברקע כדימון וקבל פקודות</translation> </message> @@ -2193,7 +2208,7 @@ Address: %4 <translation>השתמש ברשת הבדיקה</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>קבל חיבורים מבחוץ (ברירת מחדל: 1 ללא -proxy או -connect)</translation> </message> @@ -2218,12 +2233,7 @@ Address: %4 <translation>אזהרה: אנא בדוק שהתאריך והשעה של המחשב שלך נכונים! אם השעון שלך אינו נכון ביטקוין לא יעבוד כראוי.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>שגיאה אירעה במהלך קביעת פורט RPC %i להאזנה: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>אפשרויות יצירת בלוק:</translation> </message> @@ -2248,17 +2258,12 @@ Address: %4 <translation>מצא עמיתים ע"י חיפוש DNS (ברירת מחדל: 1 ללא -connect)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>מייבא בלוקים...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>כתובת לא תקינה ל -tor: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>חוצץ קבלה מירבי לכל חיבור, <n>*1000 בתים (ברירת מחדל: 5000)</translation> </message> @@ -2363,27 +2368,22 @@ Address: %4 <translation>סיסמה לחיבורי JSON-RPC</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>האזן לחיבורי JSON-RPC ב<פורט> (ברירת מחדל: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>אפשר חיבורי JSON-RPC מכתובת האינטרנט המצוינת</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>שלח פקודות לצומת ב-<ip> (ברירת מחדל: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>בצע פקודה זו כשהבלוק הטוב ביותר משתנה (%s בפקודה יוחלף בגיבוב הבלוק)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>שדרג את הארנק לפורמט העדכני</translation> </message> @@ -2408,12 +2408,12 @@ Address: %4 <translation>מידת היסודיות של אימות הבלוקים (0-6, ברירת מחדל: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>מיבא בלוקים מקובץ blk000?.dat חיצוני</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>השתמש ב-OpenSSL (https( עבור חיבורי JSON-RPC</translation> </message> @@ -2428,22 +2428,22 @@ Address: %4 <translation>מפתח פרטי של השרת (ברירת מחדל: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>צפנים קבילים (ברירת מחדל: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>הודעת העזרה הזו</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>אינו מסוגל לנעול את תיקיית הנתונים %s. כנראה שביטקוין כבר רץ.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>ביטקוין</translation> </message> @@ -2458,12 +2458,12 @@ Address: %4 <translation>התחבר דרך פרוקסי SOCKS</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>אפשר בדיקת DNS עבור -addnode, -seednode ו- -connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>טוען כתובות...</translation> </message> @@ -2493,12 +2493,12 @@ Address: %4 <translation>שגיאה בטעינת הקובץ wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>כתובת -proxy לא תקינה: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>רשת לא ידועה צוינה ב- -onlynet: '%s'</translation> </message> @@ -2518,12 +2518,12 @@ Address: %4 <translation>לא מסוגל לפתור כתובת -externalip: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>כמות לא תקינה עבור -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>שגיאה: לא ניתן להתחיל צומת</translation> </message> @@ -2533,12 +2533,12 @@ Address: %4 <translation>שגיאה: הארנק נעול, לא ניתן ליצור פעולה</translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>שגיאה: הפעולה דורשת עמלת פעולה של לפחות %s מפאת הכמות, המורכבות, או השימוש בכספים שהתקבלו לאחרונה</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>שגיאה: יצירת הפעולה נכשלה </translation> </message> @@ -2548,12 +2548,12 @@ Address: %4 <translation>שולח...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>שגיאה: הפעולה נדחתה. זה עשוי לקרות אם חלק מהמטבעות בארנק שלך כבר נוצלו, למשל אם השתמשת בעותק של הקובץ wallet.dat ומטבעות נוצלו בהעתק אך לא סומנו כמנוצלות כאן.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>כמות לא תקינה</translation> </message> @@ -2563,12 +2563,12 @@ Address: %4 <translation>אין מספיק כספים</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>טוען את אינדקס הבלוקים...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>הוסף צומת להתחברות ונסה לשמור את החיבור פתוח</translation> </message> @@ -2578,7 +2578,7 @@ Address: %4 <translation>לא ניתן לקשור ל-%s במחשב זה. ביטקוין כנראה עדיין רץ.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>מצא עמיתים תוך שימוש ב-IRC (ברירת מחדל: 0)</translation> </message> @@ -2623,7 +2623,7 @@ Address: %4 <translation>להשתמש באפשרות %s</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2641,12 +2641,12 @@ rpcpassword=%s אם הקובץ אינו קיים, צור אותו עם הרשאות קריאה לבעלים בלבד.</translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>שגיאה</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_hr.ts b/src/qt/locale/bitcoin_hr.ts index 38892552c6..9e4fe05acb 100644 --- a/src/qt/locale/bitcoin_hr.ts +++ b/src/qt/locale/bitcoin_hr.ts @@ -77,11 +77,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Potpišite poruku</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -91,12 +96,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Brisanje trenutno odabrane adrese s popisa. Samo adrese za slanje se mogu izbrisati.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Brisanje</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Jeste li sigurni da želite šifrirati svoj novčanik?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Novčanik šifriran</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin će se sada zatvoriti kako bi dovršio postupak šifriranja. Zapamtite da šifriranje vašeg novčanika ne može u potpunosti zaštititi vaše bitcoine od krađe preko zloćudnog softvera koji bi bio na vašem računalu.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>&Potpišite poruku...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Usklađivanje s mrežom ...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Pregled</translation> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Prikaži opći pregled novčanika</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transakcije</translation> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Uređivanje popisa pohranjenih adresa i oznaka</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Primanje novca</translation> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Prikaži popis adresa za primanje isplate</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Slanje novca</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Izlaz</translation> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Postavke</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Šifriraj novčanik...</translation> </message> @@ -398,42 +403,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Preuzeto %1 od %2 blokova povijesti transakcije (%3% done).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Izvoz...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Slanje novca na bitcoin adresu</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Promijeni postavke konfiguracije za bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Izvoz podataka iz trenutnog taba u datoteku</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Šifriranje ili dešifriranje novčanika</translation> </message> @@ -448,7 +438,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Promijenite lozinku za šifriranje novčanika</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -458,12 +448,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -473,7 +463,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Novčanik</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&O Bitcoinu</translation> </message> @@ -483,12 +473,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Datoteka</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Konfiguracija</translation> </message> @@ -503,7 +493,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Traka kartica</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Traka akcija</translation> </message> @@ -520,7 +510,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktivna veza na Bitcoin mrežu</numerusform><numerusform>%n aktivne veze na Bitcoin mrežu</numerusform><numerusform>%n aktivnih veza na Bitcoin mrežu</numerusform></translation> </message> @@ -598,7 +588,7 @@ Adresa:%4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1105,7 +1095,7 @@ Adresa:%4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1256,8 +1246,8 @@ Adresa:%4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Dodaj primatelja</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1286,8 +1276,8 @@ Adresa:%4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Pošalji</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1642,7 +1632,7 @@ Adresa:%4 <translation>Generiran</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Od</translation> @@ -1753,12 +1743,12 @@ Adresa:%4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, još nije bio uspješno emitiran</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>nepoznato</translation> </message> @@ -2069,7 +2059,7 @@ Adresa:%4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoin verzija</translation> </message> @@ -2084,12 +2074,12 @@ Adresa:%4 <translation>Pošalji komandu usluzi -server ili bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Prikaži komande</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Potraži pomoć za komandu</translation> </message> @@ -2164,22 +2154,47 @@ Adresa:%4 <translation>Prag za odspajanje članova koji se čudno ponašaju (default: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Broj sekundi koliko se članovima koji se čudno ponašaju neće dopustiti da se opet spoje (default: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Prihvati komande iz tekst moda i JSON-RPC</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Izvršavaj u pozadini kao uslužnik i prihvaćaj komande</translation> </message> @@ -2189,7 +2204,7 @@ Adresa:%4 <translation>Koristi test mrežu</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2214,12 +2229,7 @@ Adresa:%4 <translation>Upozorenje: Molimo provjerite jesu li datum i vrijeme na vašem računalu točni. Ako vaš sat ide krivo, Bitcoin neće raditi ispravno.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2244,17 +2254,12 @@ Adresa:%4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Nevaljala -tor adresa: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2359,27 +2364,22 @@ Adresa:%4 <translation>Lozinka za JSON-RPC veze</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Prihvaćaj JSON-RPC povezivanje na portu broj <port> (ugrađeni izbor: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Dozvoli JSON-RPC povezivanje s određene IP adrese</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Pošalji komande nodu na adresi <ip> (ugrađeni izbor: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2404,12 +2404,12 @@ Adresa:%4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Koristi OpenSSL (https) za JSON-RPC povezivanje</translation> </message> @@ -2424,22 +2424,22 @@ Adresa:%4 <translation>Uslužnikov privatni ključ (ugrađeni izbor: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Prihvaljivi načini šifriranja (ugrađeni izbor: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Ova poruka za pomoć</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Program ne može pristupiti direktoriju s datotekama %s. Bitcoin program je vjerojatno već pokrenut.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2454,12 +2454,12 @@ Adresa:%4 <translation>Poveži se kroz socks proxy</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Dozvoli DNS upite za dodavanje nodova i povezivanje</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Učitavanje adresa...</translation> </message> @@ -2489,12 +2489,12 @@ Adresa:%4 <translation>Greška kod učitavanja wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Nevaljala -proxy adresa: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2514,12 +2514,12 @@ Adresa:%4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Nevaljali iznos za opciju -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2529,12 +2529,12 @@ Adresa:%4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Ova transakcija je preko ograničenja veličine. Možete ju ipak poslati za naknadu od %1, koja se daje čvorovima koji procesiraju vaše transakcije i tako podržavate mrežu. Želite li platiti naknadu?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Greška: priprema transakcije nije uspjela</translation> </message> @@ -2544,12 +2544,12 @@ Adresa:%4 <translation>Slanje...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Generirani novčići moraju pričekati nastanak 120 blokova prije nego što ih je moguće potrošiti. Kad ste generirali taj blok, on je bio emitiran u mrežu kako bi bio dodan postojećim lancima blokova. Ako ne uspije biti dodan, njegov status bit će promijenjen u "nije prihvatljiv" i on neće biti potrošiv. S vremena na vrijeme tako nešto se može desiti ako neki drugi nod približno istovremeno generira blok.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Nevaljali iznos za opciju</translation> </message> @@ -2559,12 +2559,12 @@ Adresa:%4 <translation>Nedovoljna sredstva</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Učitavanje indeksa blokova...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Unesite nod s kojim se želite spojiti and attempt to keep the connection open</translation> </message> @@ -2574,7 +2574,7 @@ Adresa:%4 <translation>Program ne može koristiti %s na ovom računalu. Bitcoin program je vjerojatno već pokrenut.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2619,7 +2619,7 @@ Adresa:%4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2631,12 +2631,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation type="unfinished"/> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_hu.ts b/src/qt/locale/bitcoin_hu.ts index cd3dd4a7df..26785233d2 100644 --- a/src/qt/locale/bitcoin_hu.ts +++ b/src/qt/locale/bitcoin_hu.ts @@ -81,11 +81,16 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -95,12 +100,7 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>A kiválasztott cím törlése a listáról. Csak a küldő címek törölhetőek.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Törlés</translation> </message> @@ -231,24 +231,29 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation>Biztosan kódolni akarod a tárcát?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Tárca kódolva</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin will close now to finish the encryption process. Ne feledd, hogy a tárca titkosítása sem nyújt teljes védelmet az adathalász programok fertőzésével szemben.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -292,17 +297,17 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation type="unfinished"/> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Szinkronizálás a hálózattal...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Áttekintés</translation> </message> @@ -312,7 +317,7 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation>Tárca általános áttekintése</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Tranzakciók</translation> </message> @@ -332,7 +337,7 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation>Tárolt címek és címkék listájának szerkesztése</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>Érmék &fogadása</translation> </message> @@ -342,12 +347,12 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation>Kiizetést fogadó címek listája</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>Érmék &küldése</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Kilépés</translation> </message> @@ -377,7 +382,7 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation>&Opciók...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>Tárca &kódolása...</translation> </message> @@ -402,42 +407,27 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation>%1 blokk letöltve a tranzakciótörténet %2 blokkjából (%3% done).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exportálás...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Érmék küldése megadott címre</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Bitcoin konfigurációs opciók</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Jelenlegi nézet exportálása fájlba</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Tárca kódolása vagy dekódolása</translation> </message> @@ -452,7 +442,7 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation>Tárcakódoló jelszó megváltoztatása</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -462,12 +452,12 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -477,7 +467,7 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation>Tárca</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&A Bitcoinról</translation> </message> @@ -487,12 +477,12 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Fájl</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Beállítások</translation> </message> @@ -507,7 +497,7 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation>Fül eszköztár</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Parancsok eszköztár</translation> </message> @@ -524,7 +514,7 @@ Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (htt <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktív kapcsolat a Bitcoin-hálózattal</numerusform><numerusform>%n aktív kapcsolat a Bitcoin-hálózattal</numerusform></translation> </message> @@ -602,7 +592,7 @@ Cím: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1110,7 +1100,7 @@ Cím: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1261,8 +1251,8 @@ Cím: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Címzett hozzáadása</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1291,8 +1281,8 @@ Cím: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Küldés</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1648,7 +1638,7 @@ Cím: %4 <translation>Legenerálva</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Űrlap</translation> @@ -1759,12 +1749,12 @@ Cím: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, még nem sikerült elküldeni.</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>ismeretlen</translation> </message> @@ -2075,7 +2065,7 @@ Cím: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoin verzió</translation> </message> @@ -2091,13 +2081,13 @@ Cím: %4 </translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Parancsok kilistázása </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Segítség egy parancsról </translation> @@ -2179,23 +2169,48 @@ Cím: %4 <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Parancssoros és JSON-RPC parancsok elfogadása </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Háttérben futtatás daemonként és parancsok elfogadása </translation> @@ -2207,7 +2222,7 @@ Cím: %4 </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2232,12 +2247,7 @@ Cím: %4 <translation>Figyelem: Ellenőrizd, hogy helyesen van-e beállítva a gépeden a dátum és az idő. A Bitcoin nem fog megfelelően működni, ha rosszul van beállítvaaz órád.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2262,17 +2272,12 @@ Cím: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Érvénytelen -tor cím: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2379,30 +2384,24 @@ Cím: %4 </translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>JSON-RPC csatlakozásokhoz figyelendő <port> (alapértelmezett: 8332) -</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>JSON-RPC csatlakozások engedélyezése meghatározott IP-címről </translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Parancsok küldése <ip> címen működő csomóponthoz (alapértelmezett: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2429,12 +2428,12 @@ Cím: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>OpenSSL (https) használata JSON-RPC csatalkozásokhoz </translation> @@ -2452,24 +2451,24 @@ Cím: %4 </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Elfogadható rejtjelkulcsok (alapértelmezett: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH ) </translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Ez a súgó-üzenet </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Az %s adatkönyvtár nem zárható. A Bitcoin valószínűleg fut már.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2484,12 +2483,12 @@ Cím: %4 <translation>Csatlakozás SOCKS proxyn keresztül</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>DNS-kikeresés engedélyezése az addnode-nál és a connect-nél</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Címek betöltése...</translation> </message> @@ -2519,12 +2518,12 @@ Cím: %4 <translation>Hiba az wallet.dat betöltése közben</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Érvénytelen -proxy cím: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2544,12 +2543,12 @@ Cím: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Étvénytelen -paytxfee=<összeg> összeg: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2559,12 +2558,12 @@ Cím: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Ez a tranzakció túllépi a mérethatárt, de %1 tranzakciós díj ellenében így is elküldheted. Ezt a plusz összeget a tranzakcióidat feldolgozó csomópontok kapják, így magát a hálózatot támogatod vele. Hajlandó vagy megfizetni a díjat?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Hiba: nem sikerült létrehozni a tranzakciót </translation> </message> @@ -2574,12 +2573,12 @@ Cím: %4 <translation>Küldés...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Hiba: a tranzakciót elutasították. Ezt az okozhatja, ha már elköltöttél valamennyi érmét a tárcádból - például ha a wallet.dat-od egy másolatát használtad, és így az elköltés csak abban lett jelölve, de itt nem.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Étvénytelen összeg</translation> </message> @@ -2589,12 +2588,12 @@ Cím: %4 <translation>Nincs elég bitcoinod.</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Blokkindex betöltése...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Elérendő csomópont megadása and attempt to keep the connection open</translation> </message> @@ -2604,7 +2603,7 @@ Cím: %4 <translation>A %s nem elérhető ezen a gépen. A Bitcoin valószínűleg fut már.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2649,7 +2648,7 @@ Cím: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2661,12 +2660,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Hiba</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts index ea6c4c1f3a..a148d6d897 100644 --- a/src/qt/locale/bitcoin_it.ts +++ b/src/qt/locale/bitcoin_it.ts @@ -82,11 +82,16 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Firma il messaggio</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -96,12 +101,7 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Cancella l'indirizzo attualmente selezionato dalla lista. Solo indirizzi d'invio possono essere cancellati.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Cancella</translation> </message> @@ -232,24 +232,29 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>Si è sicuri di voler cifrare il portamonete?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Attenzione: tasto Blocco maiuscole attivo.</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Portamonete cifrato</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin verrà ora chiuso per finire il processo di crittazione. Ricorda che criptare il tuo portamonete non può fornire una protezione totale contro furti causati da malware che dovessero infettare il tuo computer.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Firma il &messaggio...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Sto sincronizzando con la rete...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Sintesi</translation> </message> @@ -313,7 +318,7 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>Mostra lo stato generale del portamonete</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transazioni</translation> </message> @@ -333,7 +338,7 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>Modifica la lista degli indirizzi salvati e delle etichette</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Ricevi monete</translation> </message> @@ -343,12 +348,12 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>Mostra la lista di indirizzi su cui ricevere pagamenti</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Invia monete</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Esci</translation> </message> @@ -378,7 +383,7 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>&Opzioni...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Cifra il portamonete...</translation> </message> @@ -403,42 +408,27 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>Scaricati %1 di %2 blocchi dello storico delle transazioni ( il %3% )</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Esporta...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Invia monete ad un indirizzo bitcoin</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Firma un messaggio per dimostrare di possedere questo indirizzo</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Modifica configurazione opzioni per bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Esporta i dati nella tabella corrente su un file</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Cifra o decifra il portamonete</translation> </message> @@ -453,7 +443,7 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>Cambia la passphrase per la cifratura del portamonete</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -463,12 +453,12 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,7 +468,7 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>Portamonete</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Info su Bitcoin</translation> </message> @@ -488,12 +478,12 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>&Mostra/Nascondi</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&File</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Impostazioni</translation> </message> @@ -508,7 +498,7 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>Barra degli strumenti "Tabs"</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Barra degli strumenti "Azioni"</translation> </message> @@ -525,7 +515,7 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <translation>Bitcoin client</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n connessione attiva alla rete Bitcoin</numerusform><numerusform>%n connessioni attive alla rete Bitcoin</numerusform></translation> </message> @@ -577,7 +567,7 @@ Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso <message> <location line="+5"/> <source>Confirm transaction fee</source> - <translation type="unfinished"/> + <translation>Conferma compenso transazione</translation> </message> <message> <location line="+27"/> @@ -604,7 +594,7 @@ Indirizzo: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -656,7 +646,7 @@ Indirizzo: %4 <message> <location filename="../clientmodel.cpp" line="+87"/> <source>Network Alert</source> - <translation type="unfinished"/> + <translation>Avviso di rete</translation> </message> </context> <context> @@ -733,7 +723,7 @@ Indirizzo: %4 <location filename="../guiutil.cpp" line="+419"/> <location line="+12"/> <source>Bitcoin-Qt</source> - <translation type="unfinished"/> + <translation>Bitcoin-Qt</translation> </message> <message> <location line="-12"/> @@ -748,7 +738,7 @@ Indirizzo: %4 <message> <location line="+1"/> <source>command-line options</source> - <translation type="unfinished"/> + <translation>opzioni riga di comando</translation> </message> <message> <location line="+4"/> @@ -758,7 +748,7 @@ Indirizzo: %4 <message> <location line="+1"/> <source>Set language, for example "de_DE" (default: system locale)</source> - <translation type="unfinished"/> + <translation>Imposta lingua, ad esempio "it_IT" (predefinita: lingua di sistema)</translation> </message> <message> <location line="+1"/> @@ -862,7 +852,7 @@ Indirizzo: %4 <message> <location line="+7"/> <source>SOCKS &Version:</source> - <translation type="unfinished"/> + <translation>SOCKS &Version:</translation> </message> <message> <location line="+13"/> @@ -1034,7 +1024,7 @@ Indirizzo: %4 <location filename="../overviewpage.cpp" line="+112"/> <location line="+1"/> <source>out of sync</source> - <translation type="unfinished"/> + <translation>fuori sincrono</translation> </message> </context> <context> @@ -1087,7 +1077,7 @@ Indirizzo: %4 <message> <location line="+25"/> <source>Save QR Code</source> - <translation type="unfinished"/> + <translation>Salva codice QR</translation> </message> <message> <location line="+0"/> @@ -1112,14 +1102,14 @@ Indirizzo: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> - <translation type="unfinished"/> + <translation>N/D</translation> </message> <message> <location line="-217"/> <source>Client version</source> - <translation type="unfinished"/> + <translation>Versione client</translation> </message> <message> <location line="-45"/> @@ -1134,17 +1124,17 @@ Indirizzo: %4 <message> <location line="+49"/> <source>Startup time</source> - <translation type="unfinished"/> + <translation>Tempo di avvio</translation> </message> <message> <location line="+29"/> <source>Network</source> - <translation type="unfinished"/> + <translation>Rete</translation> </message> <message> <location line="+7"/> <source>Number of connections</source> - <translation type="unfinished"/> + <translation>Numero connessioni</translation> </message> <message> <location line="+23"/> @@ -1154,22 +1144,22 @@ Indirizzo: %4 <message> <location line="+23"/> <source>Block chain</source> - <translation type="unfinished"/> + <translation>Block chain</translation> </message> <message> <location line="+7"/> <source>Current number of blocks</source> - <translation type="unfinished"/> + <translation>Numero attuale di blocchi</translation> </message> <message> <location line="+23"/> <source>Estimated total blocks</source> - <translation type="unfinished"/> + <translation>Numero totale stimato di blocchi</translation> </message> <message> <location line="+23"/> <source>Last block time</source> - <translation type="unfinished"/> + <translation>Ora dell blocco piu recente</translation> </message> <message> <location line="+52"/> @@ -1224,12 +1214,12 @@ Indirizzo: %4 <message> <location line="+102"/> <source>Clear console</source> - <translation type="unfinished"/> + <translation>Svuota console</translation> </message> <message> <location filename="../rpcconsole.cpp" line="-33"/> <source>Welcome to the Bitcoin RPC console.</source> - <translation type="unfinished"/> + <translation>Benvenuto nella console RPC di Bitcoin</translation> </message> <message> <location line="+1"/> @@ -1239,7 +1229,7 @@ Indirizzo: %4 <message> <location line="+1"/> <source>Type <b>help</b> for an overview of available commands.</source> - <translation type="unfinished"/> + <translation>Scrivi <b>help</b> per un riassunto dei comandi disponibili</translation> </message> </context> <context> @@ -1263,8 +1253,8 @@ Indirizzo: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Aggiungi beneficiario</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1293,8 +1283,8 @@ Indirizzo: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Spedisci</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1649,7 +1639,7 @@ Indirizzo: %4 <translation>Generato</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Da</translation> @@ -1760,12 +1750,12 @@ Indirizzo: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, non è stato ancora trasmesso con successo</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>sconosciuto</translation> </message> @@ -1997,7 +1987,7 @@ Indirizzo: %4 <message> <location line="+1"/> <source>Show transaction details</source> - <translation type="unfinished"/> + <translation>Mostra i dettagli della transazione</translation> </message> <message> <location line="+142"/> @@ -2076,7 +2066,7 @@ Indirizzo: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Versione di Bitcoin</translation> </message> @@ -2092,13 +2082,13 @@ Indirizzo: %4 </translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Lista comandi </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Aiuto su un comando </translation> @@ -2167,7 +2157,7 @@ Indirizzo: %4 <message> <location line="+64"/> <source>Specify your own public address</source> - <translation type="unfinished"/> + <translation>Specifica il tuo indirizzo pubblico</translation> </message> <message> <location line="-75"/> @@ -2180,23 +2170,48 @@ Indirizzo: %4 <translation>Soglia di disconnessione dei peer di cattiva qualità (default: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Numero di secondi di sospensione che i peer di cattiva qualità devono trascorrere prima di riconnettersi (default: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accetta da linea di comando e da comandi JSON-RPC </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Esegui in background come demone e accetta i comandi </translation> @@ -2208,7 +2223,7 @@ Indirizzo: %4 </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2233,12 +2248,7 @@ Indirizzo: %4 <translation>Attenzione: si prega di controllare che la data del computer e l'ora siano corrette. Se il vostro orologio è sbagliato Bitcoin non funziona correttamente.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2263,17 +2273,12 @@ Indirizzo: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Indirizzo -tor non valido: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Buffer di ricezione massimo per connessione, <n>*1000 byte (default: 5000)</translation> </message> @@ -2380,30 +2385,24 @@ Indirizzo: %4 </translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Attendi le connessioni JSON-RPC su <porta> (default: 8332) -</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Consenti connessioni JSON-RPC dall'indirizzo IP specificato </translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Inviare comandi al nodo in esecuzione su <ip> (default: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Aggiorna il wallet all'ultimo formato</translation> </message> @@ -2430,12 +2429,12 @@ Indirizzo: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Utilizzare OpenSSL (https) per le connessioni JSON-RPC </translation> @@ -2453,24 +2452,24 @@ Indirizzo: %4 </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Cifrari accettabili (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) </translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Questo messaggio di aiuto </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Non è possibile ottenere i dati sulla directory %s. Probabilmente Bitcoin è già in esecuzione.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2485,13 +2484,13 @@ Indirizzo: %4 <translation>Connessione tramite socks proxy</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Consenti ricerche DNS per aggiungere nodi e collegare </translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Caricamento indirizzi...</translation> </message> @@ -2521,12 +2520,12 @@ Indirizzo: %4 <translation>Errore caricamento wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Indirizzo -proxy non valido: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2546,14 +2545,14 @@ Indirizzo: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Importo non valido per -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> - <translation type="unfinished"/> + <translation>Errore: impossibile inizializzare il nodo</translation> </message> <message> <location line="-1"/> @@ -2561,12 +2560,12 @@ Indirizzo: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Questa transazione è superiore al limite di dimensione. È comunque possibile inviarla con una commissione di %1, che va ai nodi che processano la tua transazione e contribuisce a sostenere la rete. Vuoi pagare la commissione?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Errore: creazione della transazione fallita </translation> </message> @@ -2576,12 +2575,12 @@ Indirizzo: %4 <translation>Invio...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Errore: la transazione è stata rifiutata. Ciò accade se alcuni bitcoin nel portamonete sono stati già spesi, ad esempio se è stata usata una copia del file wallet.dat e i bitcoin sono stati spesi dalla copia ma non segnati come spesi qui.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Importo non valido</translation> </message> @@ -2591,12 +2590,12 @@ Indirizzo: %4 <translation>Fondi insufficienti</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Caricamento dell'indice del blocco...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Elérendő csomópont megadása and attempt to keep the connection open</translation> </message> @@ -2606,7 +2605,7 @@ Indirizzo: %4 <translation>Impossibile collegarsi alla %s su questo computer. Probabilmente Bitcoin è già in esecuzione.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2651,7 +2650,7 @@ Indirizzo: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2663,12 +2662,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Errore</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_lt.ts b/src/qt/locale/bitcoin_lt.ts index 51f301775a..ba3873ba84 100644 --- a/src/qt/locale/bitcoin_lt.ts +++ b/src/qt/locale/bitcoin_lt.ts @@ -81,11 +81,16 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Pasirašyti žinutę</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Patikrinkite žinutę, jog įsitikintumėte, kad ją pasirašė nurodytas Bitcoin adresas</translation> </message> @@ -95,12 +100,7 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>&Tikrinti žinutę</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Pašalinti iš sąrašo pažymėtą adresą(gali būti pašalinti tiktai adresų knygelės įrašai).</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Trinti</translation> </message> @@ -231,24 +231,29 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Ar tikrai norite šifruoti savo piniginę?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Įspėjimas: įjungtas Caps Lock klavišas!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Piniginė užšifruota</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin dabar užsidarys šifravimo proceso pabaigai. Atminkite, kad piniginės šifravimas negali pilnai apsaugoti bitcoinų vagysčių kai tinkle esančios kenkėjiškos programos patenka į jūsų kompiuterį.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -292,17 +297,17 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Pasirašyti ži&nutę...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Sinchronizavimas su tinklu ...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Apžvalga</translation> </message> @@ -312,7 +317,7 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Rodyti piniginės bendrą apžvalgą</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Sandoriai</translation> </message> @@ -332,7 +337,7 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Redaguoti išsaugotus adresus bei žymes</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Gauti monetas</translation> </message> @@ -342,12 +347,12 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Parodyti adresų sąraša mokėjimams gauti</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Siųsti monetas</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Išeiti</translation> </message> @@ -377,7 +382,7 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>&Parinktys...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Užšifruoti piniginę...</translation> </message> @@ -402,42 +407,27 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Atsisiuntė %1 iš %2 sandorių istorijos blokų (%3% done).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Eksportuoti...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Siųsti monetas Bitcoin adresui</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Pasirašykite žinutę, kad įrodytume, jog esate Bitcoin adreso savininkas</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Patikrinkite žinutę, jog įsitikintumėte, kad ją pasirašė nurodytas Bitcoin adresas</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>&Parašai</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Keisti bitcoin konfigūracijos galimybes</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Užšifruoti ar iššifruoti piniginę</translation> </message> @@ -452,7 +442,7 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Pakeisti slaptafrazę naudojamą piniginės užšifravimui</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Derinimo langas</translation> </message> @@ -462,12 +452,12 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Atverti derinimo ir diagnostikos konsolę</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Tikrinti žinutę...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -477,7 +467,7 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Piniginė</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Apie Bitcoin</translation> </message> @@ -487,12 +477,12 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>&Rodyti / Slėpti</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Failas</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Nustatymai</translation> </message> @@ -507,7 +497,7 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Kortelių įrankinė</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Veiksmų įrankinė</translation> </message> @@ -524,7 +514,7 @@ Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www. <translation>Bitcoin klientas</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n Bitcoin tinklo aktyvus ryšys</numerusform><numerusform>%n Bitcoin tinklo aktyvūs ryšiai</numerusform><numerusform>%n Bitcoin tinklo aktyvūs ryšiai</numerusform></translation> </message> @@ -601,7 +591,7 @@ Tipas: %3 Adresas: %4</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>URI apdorojimas</translation> @@ -1108,7 +1098,7 @@ Adresas: %4</translation> <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>nėra</translation> </message> @@ -1259,8 +1249,8 @@ Adresas: %4</translation> </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&A Pridėti gavėją</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1289,8 +1279,8 @@ Adresas: %4</translation> </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Siųsti</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1645,7 +1635,7 @@ Adresas: %4</translation> <translation>Sugeneruotas</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Nuo</translation> @@ -1756,12 +1746,12 @@ Adresas: %4</translation> <translation>netiesa</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, transliavimas dar nebuvo sėkmingas</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>nežinomas</translation> </message> @@ -2072,7 +2062,7 @@ Adresas: %4</translation> <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoin versija</translation> </message> @@ -2087,12 +2077,12 @@ Adresas: %4</translation> <translation>Siųsti komandą serveriui arba bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Komandų sąrašas</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Suteikti pagalba komandai</translation> </message> @@ -2167,22 +2157,47 @@ Adresas: %4</translation> <translation>Atjungimo dėl netinkamo kolegų elgesio riba (pagal nutylėjimą: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Sekundžių kiekis eikiamas palaikyti ryšį dėl lygiarangių nestabilumo (pagal nutylėjimą: 86.400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Priimti komandinę eilutę ir JSON-RPC komandas</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Dirbti fone kaip šešėlyje ir priimti komandas</translation> </message> @@ -2192,7 +2207,7 @@ Adresas: %4</translation> <translation>Naudoti testavimo tinklą</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2217,12 +2232,7 @@ Adresas: %4</translation> <translation>Įspėjimas: Patikrinkite, kad kompiuterio data ir laikas yra teisingi.Jei Jūsų laikrodis neteisingai nustatytas Bitcoin, veiks netinkamai.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2247,17 +2257,12 @@ Adresas: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Importuojami blokai...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Neteisingas tor adresas: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Maksimalus buferis priėmimo sujungimui <n>*1000 bitų (pagal nutylėjimą: 5000)</translation> </message> @@ -2362,27 +2367,22 @@ Adresas: %4</translation> <translation>Slaptažodis JSON-RPC sujungimams</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Klausymas JSON-RPC sujungimui prijungčiai <port> (pagal nutylėjimą: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Leisti JSON-RPC tik iš nurodytų IP adresų</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Siųsti komandą mazgui dirbančiam <ip> (pagal nutylėjimą: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2407,12 +2407,12 @@ Adresas: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Naudoti OpenSSL (https) jungimuisi JSON-RPC </translation> </message> @@ -2427,22 +2427,22 @@ Adresas: %4</translation> <translation>Serverio privatus raktas (pagal nutylėjimą: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Priimtini šifrai (pagal nutylėjimą: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Pagelbos žinutė</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Negali gauti duomenų katalogo %s rakto. Bitcoin tikriausiai jau veikia.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2457,12 +2457,12 @@ Adresas: %4</translation> <translation>Jungtis per socks tarpinį serverį</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Leisti DNS paiešką sujungimui ir mazgo pridėjimui</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Užkraunami adresai...</translation> </message> @@ -2492,12 +2492,12 @@ Adresas: %4</translation> <translation> wallet.dat pakrovimo klaida</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Neteisingas proxy adresas: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2517,12 +2517,12 @@ Adresas: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Neteisinga suma -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2532,12 +2532,12 @@ Adresas: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Šis sandoris viršija leistiną dydį. Jūs galite įvykdyti jį papildomai sumokėję %1 mokesčių, kurie bus išsiųsti tais pačiais mazgais kuriais vyko sandoris ir padės palaikyti tinklą. Ar jūs norite apmokėti papildomą mokestį?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>KLAIDA:nepavyko sudaryti sandorio</translation> </message> @@ -2547,12 +2547,12 @@ Adresas: %4</translation> <translation>Siunčiama...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Klaida: sandoris buvo atmestas.Tai gali įvykti, jei kai kurios monetos iš jūsų piniginėje jau buvo panaudotos, pvz. jei naudojote wallet.dat kopiją ir monetos buvo išleistos kopijoje, bet nepažymėtos kaip skirtos išleisti čia.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Neteisinga suma</translation> </message> @@ -2562,12 +2562,12 @@ Adresas: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Įkeliamas blokų indeksas...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Pridėti mazgą prie sujungti su and attempt to keep the connection open</translation> </message> @@ -2577,7 +2577,7 @@ Adresas: %4</translation> <translation>Nepavyko susieti šiame kompiuteryje prievado %s. Bitcoin tikriausiai jau veikia.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2622,7 +2622,7 @@ Adresas: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2634,12 +2634,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Klaida</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts index 67779480e2..01311a46c8 100644 --- a/src/qt/locale/bitcoin_nb.ts +++ b/src/qt/locale/bitcoin_nb.ts @@ -82,11 +82,16 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Signér Melding</translation> + <source>Sign &Message</source> + <translation>Signér &Melding</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Verifiser en melding for å være sikker på at den ble signert av en angitt Bitcoin-adresse</translation> </message> @@ -96,12 +101,7 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>&Verifiser Melding</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Slett den valgte adressen fra listen. Bare adresser for sending kan slettes.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Slett</translation> </message> @@ -232,24 +232,29 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Er du sikker på at du vil kryptere lommeboken?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Advarsel: Caps Lock er på !</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Lommebok kryptert</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin vil nå lukkes for å fullføre krypteringsprosessen. Husk at kryptering av lommeboken ikke fullt ut kan beskytte dine bitcoins fra å bli stjålet om skadevare infiserer datamaskinen.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Signer &melding...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synkroniserer med nettverk...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Oversikt</translation> </message> @@ -313,7 +318,7 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Vis generell oversikt over lommeboken</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transaksjoner</translation> </message> @@ -333,7 +338,7 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Rediger listen over adresser og deres merkelapper</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Motta bitcoins</translation> </message> @@ -343,12 +348,12 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Vis listen over adresser for mottak av betalinger</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Send bitcoins</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Avslutt</translation> </message> @@ -378,7 +383,7 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>&Innstillinger...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Krypter Lommebok...</translation> </message> @@ -403,42 +408,27 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Lastet ned %1 av %2 blokker med transaksjonshistorikk (%3% ferdig).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Eksporter...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Send til en Bitcoin-adresse</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Signer en melding for å bevise at du eier en Bitcoin-adresse</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Verifiser en melding for å være sikker på at den ble signert av en angitt Bitcoin-adresse</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>S&ignaturer</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Endre oppsett for Bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Eksporter data fra nåværende fane til fil</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Krypter eller dekrypter lommebok</translation> </message> @@ -453,7 +443,7 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Endre adgangsfrasen brukt for kryptering av lommebok</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Feilsøkingsvindu</translation> </message> @@ -463,12 +453,12 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Åpne konsoll for feilsøk og diagnostikk</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Verifiser melding...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,7 +468,7 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Lommebok</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Om Bitcoin</translation> </message> @@ -488,12 +478,12 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>&Gjem / vis</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Fil</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Innstillinger</translation> </message> @@ -508,7 +498,7 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Verktøylinje for faner</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Verktøylinje for handlinger</translation> </message> @@ -525,7 +515,7 @@ Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i <translation>Bitcoinklient</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktiv forbindelse til Bitcoin-nettverket</numerusform><numerusform>%n aktive forbindelser til Bitcoin-nettverket</numerusform></translation> </message> @@ -603,7 +593,7 @@ Adresse: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>URI håndtering</translation> @@ -1111,7 +1101,7 @@ Adresse: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>-</translation> </message> @@ -1262,8 +1252,8 @@ Adresse: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Legg til Mottaker</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1292,8 +1282,8 @@ Adresse: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Send</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1648,7 +1638,7 @@ Adresse: %4 <translation>Generert</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Fra</translation> @@ -1759,12 +1749,12 @@ Adresse: %4 <translation>usann</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, har ikke blitt kringkastet uten problemer enda.</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>ukjent</translation> </message> @@ -2075,7 +2065,7 @@ Adresse: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoin versjon</translation> </message> @@ -2090,12 +2080,12 @@ Adresse: %4 <translation>Send kommando til -server eller bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>List opp kommandoer</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Vis hjelpetekst for en kommando</translation> </message> @@ -2170,22 +2160,47 @@ Adresse: %4 <translation>Grenseverdi for å koble fra noder med dårlig oppførsel (standardverdi: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Antall sekunder noder med dårlig oppførsel hindres fra å koble til på nytt (standardverdi: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Koble fra blokk og adresse databaser. Gir tregere avslutning (standardverdi: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Ta imot kommandolinje- og JSON-RPC-kommandoer</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Kjør i bakgrunnen som daemon og ta imot kommandoer</translation> </message> @@ -2195,7 +2210,7 @@ Adresse: %4 <translation>Bruk testnettverket</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Ta imot tilkoblinger fra utsiden (standardverdi: 1 hvis uten -proxy eller -connect)</translation> </message> @@ -2220,12 +2235,7 @@ Adresse: %4 <translation>Advarsel: Vennligst undersøk at din datamaskin har riktig dato og klokkeslett! Hvis klokken er stilt feil vil ikke Bitcoin fungere riktig.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>En feil oppstod ved opprettelse av RPC port %i for lytting: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Valg for opprettelse av blokker:</translation> </message> @@ -2250,17 +2260,12 @@ Adresse: %4 <translation>Finn andre noder gjennom DNS-oppslag (standardverdi: 1 med mindre -connect er oppgit)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Ugyldig -tor adresse: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Maks mottaksbuffer per forbindelse, <n>*1000 bytes (standardverdi: 5000)</translation> </message> @@ -2365,27 +2370,22 @@ Adresse: %4 <translation>Passord for JSON-RPC forbindelser</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Lytt etter JSON-RPC tilkoblinger på <port> (standardverdi: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Tillat JSON-RPC tilkoblinger fra angitt IP-adresse</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Send kommandoer til node på <ip> (standardverdi: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Eksekvér kommando når beste blokk endrer seg (%s i kommandoen erstattes med blokkens hash)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Oppgradér lommebok til nyeste format</translation> </message> @@ -2410,12 +2410,12 @@ Adresse: %4 <translation>Hvor grundig verifisering av blokker gjøres (0-6, standardverdi: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Importerer blokker fra ekstern blk000?.dat fil</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Bruk OpenSSL (https) for JSON-RPC forbindelser</translation> </message> @@ -2430,22 +2430,22 @@ Adresse: %4 <translation>Servers private nøkkel (standardverdi: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Akseptable krypteringsmetoder (standardverdi: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Denne hjelpemeldingen</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Kunne ikke låse datamappen %s. Bitcoin kjører sannsynligvis allerede.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2460,12 +2460,12 @@ Adresse: %4 <translation>Koble til gjennom socks proxy</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Tillat DNS oppslag for -addnode, -seednode og -connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Laster adresser...</translation> </message> @@ -2495,12 +2495,12 @@ Adresse: %4 <translation>Feil ved lasting av wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Ugyldig -proxy adresse: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Ukjent nettverk angitt i -onlynet '%s'</translation> </message> @@ -2520,12 +2520,12 @@ Adresse: %4 <translation>Kunne ikke slå opp -externalip adresse: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Ugyldig beløp for -paytxfee=<beløp>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Feil: kunne ikke starte node</translation> </message> @@ -2535,12 +2535,12 @@ Adresse: %4 <translation>Feil: Lommebok låst, kan ikke opprette transaksjon </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Feil: Denne transaksjonen krever et gebyr på minst %s pga. beløpet, kompleksiteten, eller bruk av nylig mottatte midler </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Feil: Opprettelse av transaksjon feilet </translation> </message> @@ -2550,12 +2550,12 @@ Adresse: %4 <translation>Sender...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Feil: Transaksjonen ble avvist. Dette kan skje hvis noen av myntene i lommeboken allerede var brukt, f.eks. hvis du kopierte wallet.dat og mynter ble brukt i kopien uten å bli markert brukt her.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Ugyldig beløp</translation> </message> @@ -2565,12 +2565,12 @@ Adresse: %4 <translation>Utilstrekkelige midler</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Laster blokkindeks...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Legg til node for tilkobling og hold forbindelsen åpen</translation> </message> @@ -2580,7 +2580,7 @@ Adresse: %4 <translation>Kan ikke binde til %s på denne datamaskinen. Sannsynligvis kjører Bitcoin allerede.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Finn andre noder via internet relay chat (standardverdi: 0)</translation> </message> @@ -2625,7 +2625,7 @@ Adresse: %4 <translation>For å bruke %s opsjonen</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2644,12 +2644,12 @@ Hvis filen ikke finnes, opprett den med leserettighet kun for eier av filen. </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Feil</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index 2463a7ede0..e9024bab58 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -83,11 +83,16 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation>&Onderteken Bericht</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>Verwijder het geselecteerde adres van de lijst</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Controleer een bericht om te verifiëren dat het gespecificeerde Bitcoinadres het bericht heeft ondertekend.</translation> </message> @@ -97,12 +102,7 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>&Verifiëer Bericht</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Verwijder het huidige geselecteerde adres van de lijst. Alleen zend-adressen kunnen verwijderd worden, niet uw ontvangstadressen.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Verwijder</translation> </message> @@ -233,24 +233,29 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>Weet u zeker dat u uw portemonnee wilt versleutelen?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>BELANGRIJK: Elke eerder gemaakte backup van uw portemonneebestand dient u te vervangen door het nieuw gegenereerde, versleutelde portemonneebestand. Om veiligheidsredenen zullen eerdere backups van het niet-versleutelde portemonneebestand onbruikbaar worden zodra u uw nieuwe, versleutelde, portemonnee begint te gebruiken.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Waarschuwing: De Caps-Lock-toets staat aan!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Portemonnee versleuteld</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin zal nu afsluiten om het versleutelingsproces te voltooien. Onthoud dat het versleutelen van uw portemonnee u niet volledig kan beschermen: Malware kan uw computer infecteren en uw bitcoins stelen.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -294,17 +299,17 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>&Onderteken bericht...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synchroniseren met netwerk...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Overzicht</translation> </message> @@ -314,7 +319,7 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>Toon algemeen overzicht van de portemonnee</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transacties</translation> </message> @@ -334,7 +339,7 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>Bewerk de lijst van opgeslagen adressen en labels</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Ontvang munten</translation> </message> @@ -344,12 +349,12 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>Toon lijst van adressen om betalingen mee te ontvangen</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Verstuur munten</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Afsluiten</translation> </message> @@ -379,7 +384,7 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>O&pties...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Versleutel Portemonnee...</translation> </message> @@ -404,42 +409,27 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>%1 van %2 blokken van transactiehistorie opgehaald (%3% klaar).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exporteer...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Verstuur munten naar een Bitcoinadres</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Onderteken een bericht om te bewijzen dat u een bepaald Bitcoinadres bezit</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Controleer een bericht om te verifiëren dat het gespecificeerde Bitcoinadres het bericht heeft ondertekend.</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>&Handtekeningen</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Wijzig instellingen van Bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Exporteer de data in de huidige tab naar een bestand</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Versleutel of ontsleutel portemonnee</translation> </message> @@ -454,7 +444,7 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>wijzig het wachtwoord voor uw portemonneversleuteling</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Debugscherm</translation> </message> @@ -464,12 +454,12 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>Open debugging en diagnostische console</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Verifiëer bericht...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -479,7 +469,7 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>Portemonnee</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Over Bitcoin</translation> </message> @@ -489,12 +479,12 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>&Toon / Verberg</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Bestand</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Instellingen</translation> </message> @@ -509,7 +499,7 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>Tab-werkbalk</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Actie-werkbalk</translation> </message> @@ -526,7 +516,7 @@ Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in d <translation>Bitcoin client</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n actieve connectie naar Bitcoinnetwerk</numerusform><numerusform>%n actieve connecties naar Bitcoinnetwerk</numerusform></translation> </message> @@ -604,7 +594,7 @@ Adres: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>URI-behandeling</translation> @@ -1112,7 +1102,7 @@ Adres: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>N.v.t.</translation> </message> @@ -1263,7 +1253,7 @@ Adres: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation>Voeg &Ontvanger Toe</translation> </message> <message> @@ -1293,7 +1283,7 @@ Adres: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation>&Verstuur</translation> </message> <message> @@ -1649,7 +1639,7 @@ Adres: %4 <translation>Gegenereerd</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Van</translation> @@ -1760,12 +1750,12 @@ Adres: %4 <translation>onwaar</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, is nog niet met succes uitgezonden</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>onbekend</translation> </message> @@ -2076,7 +2066,7 @@ Adres: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoinversie</translation> </message> @@ -2092,13 +2082,13 @@ Adres: %4 </translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>List van commando's </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Toon hulp voor een commando </translation> @@ -2180,23 +2170,48 @@ Adres: %4 <translation>Drempel om verbinding te verbreken naar zich misdragende peers (standaard: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Aantal seconden dat zich misdragende peers niet opnieuw mogen verbinden (standaard: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>Er is een fout opgetreden tijdens het instellen van de inkomende RPC-poort %i op IPv6, terugval naar IPv4: %s</translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>Er is een fout opgetreden tijdens het instellen van de inkomende RPC-poort %u op IPv4: %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Ontkoppel blok- en adresdatabases. Verhoogt afsluittijd (standaard: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>Wacht op JSON-RPC-connecties op poort <port> (standaard: 8332 of testnet: 18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Aanvaard commandoregel en JSON-RPC commando's </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>Bezig met importeren van blokkenketen-databestand.</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>Bezig met importeren van bootstap-blokkenketen-databestand.</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Draai in de achtergrond als daemon en aanvaard commando's </translation> @@ -2208,7 +2223,7 @@ Adres: %4 </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accepteer verbindingen van buitenaf (standaard: 1 als geen -proxy of -connect is opgegeven)</translation> </message> @@ -2233,12 +2248,7 @@ Adres: %4 <translation>Waarschuwing: Controleer dat de datum en tijd op uw computer correct zijn ingesteld. Als uw klok fout staat zal Bitcoin niet correct werken.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Er is een fout opgetreden tijdens het opzetten van de inkomende RPC-poort %i: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Blokcreatie-opties:</translation> </message> @@ -2263,17 +2273,12 @@ Adres: %4 <translation>Vind andere nodes d.m.v. DNS-naslag (standaard: 1 tenzij -connect)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Blokken aan het importeren...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Ongeldig -tor adres: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Maximum per-connectie ontvangstbuffer, <n>*1000 bytes (standaard: 5000)</translation> </message> @@ -2380,30 +2385,24 @@ Adres: %4 </translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Luister voor JSON-RPC verbindingen op <poort> (standaard: 8332) -</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Sta JSON-RPC verbindingen van opgegeven IP adres toe </translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Verstuur commando's naar proces dat op <ip> draait (standaard: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Voer commando uit zodra het beste blok verandert (%s in cmd wordt vervangen door blockhash)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Vernieuw portemonnee naar nieuwste versie</translation> </message> @@ -2429,12 +2428,12 @@ Adres: %4 <translation>De grondigheid van de blokverificatie (0-6, standaard: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Importeert blokken van extern blk000?.dat bestand</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Gebruik OpenSSL (https) voor JSON-RPC verbindingen </translation> @@ -2452,23 +2451,23 @@ Adres: %4 </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Aanvaardbare ciphers (standaard: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Dit helpbericht </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Kan geen lock op de datamap %s verkrijgen. Bitcoin draait vermoedelijk reeds.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2483,12 +2482,12 @@ Adres: %4 <translation>Verbind via een socks-proxy</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Sta DNS-naslag toe voor -addnode, -seednode en -connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Adressen aan het laden...</translation> </message> @@ -2518,12 +2517,12 @@ Adres: %4 <translation>Fout bij laden wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Ongeldig -proxy adres: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Onbekend netwerk gespecificeerd in -onlynet: '%s'</translation> </message> @@ -2543,12 +2542,12 @@ Adres: %4 <translation>Kan -externlip adres niet herleiden: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Ongeldig bedrag voor -paytxfee=<bedrag>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Fout: Kon node niet starten</translation> </message> @@ -2558,12 +2557,12 @@ Adres: %4 <translation>Fout: Portemonnee gesloten, transactie maken niet mogelijk </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Fout: Deze transactie heeft transactiekosten nodig van tenminste %s, vanwege zijn grootte, ingewikkeldheid, of het gebruik van onlangs ontvangen munten </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Fout: Aanmaak transactie mislukt</translation> </message> @@ -2573,12 +2572,12 @@ Adres: %4 <translation>Aan het versturen...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Fout: De transactie was afgewezen. Dit kan gebeuren als u eerder uitgegeven munten opnieuw wilt versturen, zoals wanneer u een kopie van uw wallet.dat heeft gebruikt en in de kopie deze munten zijn gemarkeerd als uitgegeven, maar in de huidige nog niet.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Ongeldig aantal</translation> </message> @@ -2588,12 +2587,12 @@ Adres: %4 <translation>Ontoereikend saldo</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Blokindex aan het laden...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Voeg een knooppunt om te verbinden toe en probeer de verbinding open te houden</translation> </message> @@ -2603,7 +2602,7 @@ Adres: %4 <translation>Niet in staat om aan %s te binden op deze computer. Bitcoin draait vermoedelijk reeds.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Vind anderen door middel van Internet Relay Chat (standaard: 0)</translation> </message> @@ -2648,7 +2647,7 @@ Adres: %4 <translation>Om de %s optie te gebruiken</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2667,12 +2666,12 @@ Als het bestand niet bestaat, maak het aan met een alleen-lezen-permissie. </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Fout</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts index 865ea62067..b5a0e5de4e 100644 --- a/src/qt/locale/bitcoin_pl.ts +++ b/src/qt/locale/bitcoin_pl.ts @@ -15,7 +15,7 @@ <message> <location line="+41"/> <source>Copyright © 2009-2012 The Bitcoin developers</source> - <translation type="unfinished"/> + <translation>Copyright © 2009-2012 The Bitcoin developers</translation> </message> <message> <location line="+13"/> @@ -82,11 +82,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>Podpi&sz Wiadomość</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Zweryfikuj wiadomość, aby upewnić się, że została podpisana odpowiednim adresem Bitcoin.</translation> </message> @@ -96,12 +101,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Zweryfikuj wiadomość</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Usuń aktualnie wybrany adres z listy. Tylko adresy nadawcze mogą być usunięte.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Usuń</translation> </message> @@ -232,24 +232,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Jesteś pewien, że chcesz zaszyfrować swój portfel?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Uwaga: Klawisz Caps Lock jest włączony</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Portfel zaszyfrowany</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Program Bitcoin zamknie się aby dokończyć proces szyfrowania. Pamiętaj, że szyfrowanie portfela nie zabezpiecza w pełni Twoich bitcoinów przed kradzieżą przez wirusy lub trojany mogące zainfekować Twój komputer.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Podpisz wiado&mość...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synchronizacja z siecią...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>P&odsumowanie</translation> </message> @@ -313,7 +318,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Pokazuje ogólny zarys portfela</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transakcje</translation> </message> @@ -333,7 +338,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Edytuj listę zapisanych adresów i i etykiet</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>Odbie&rz monety</translation> </message> @@ -343,12 +348,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Pokaż listę adresów do otrzymywania płatności</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>Wy&syłka monet</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Zakończ</translation> </message> @@ -378,7 +383,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Opcje...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>Zaszyfruj Portf&el</translation> </message> @@ -403,42 +408,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Pobrano %1 z %2 bloków historii transakcji(%3% ukończono).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Eksportuj...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Wyślij monety na adres Bitcoin</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Podpisz wiadomość aby dowieść, że ten adres jest twój</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Zweryfikuj wiadomość, aby upewnić się, że została podpisana odpowiednim adresem Bitcoin.</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>P&odpisy</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Zmienia opcje konfiguracji bitcoina</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Eksportuj dane z aktywnej karty do pliku</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Zaszyfruj lub odszyfruj portfel</translation> </message> @@ -453,7 +443,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Zmień hasło użyte do szyfrowania portfela</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Okno debudowania</translation> </message> @@ -463,12 +453,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Otwórz konsolę debugowania i diagnostyki</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Zweryfikuj wiadomość...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,7 +468,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Portfel</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>O Bitcoin</translation> </message> @@ -488,12 +478,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Pokaż / Ukryj</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Plik</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>P&referencje</translation> </message> @@ -508,7 +498,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Pasek zakładek</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Pasek akcji</translation> </message> @@ -525,7 +515,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Bitcoin klient</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktywne połączenie do sieci Bitcoin</numerusform><numerusform>%n aktywne połączenia do sieci Bitcoin</numerusform><numerusform>%n aktywnych połączeń do sieci Bitcoin</numerusform></translation> </message> @@ -603,7 +593,7 @@ Adres: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>Obsługa URI</translation> @@ -1110,7 +1100,7 @@ Adres: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>NIEDOSTĘPNE</translation> </message> @@ -1261,8 +1251,8 @@ Adres: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Dodaj Odbiorcę</translation> + <source>Add &Recipient</source> + <translation>Dodaj Odbio&rce</translation> </message> <message> <location line="+20"/> @@ -1291,8 +1281,8 @@ Adres: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>Wy&syłka</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1550,7 +1540,7 @@ Adres: %4 <message> <location line="-73"/> <source>Wallet unlock was cancelled.</source> - <translation type="unfinished"/> + <translation>Odblokowanie portfela zostało anulowane.</translation> </message> <message> <location line="+8"/> @@ -1604,12 +1594,12 @@ Adres: %4 <message numerus="yes"> <location line="-2"/> <source>Open for %n block(s)</source> - <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation> + <translation><numerusform>Otwórz dla %n bloku</numerusform><numerusform>Otwórz dla %n bloków</numerusform><numerusform>Otwórz dla %n bloków</numerusform></translation> </message> <message> <location line="+8"/> <source>%1/offline</source> - <translation type="unfinished"/> + <translation>%1/offline</translation> </message> <message> <location line="+2"/> @@ -1629,7 +1619,7 @@ Adres: %4 <message numerus="yes"> <location line="+7"/> <source>, broadcast through %n node(s)</source> - <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation> + <translation><numerusform>, emitowany przez %n węzeł</numerusform><numerusform>, emitowany przez %n węzły</numerusform><numerusform>, emitowany przez %n węzłów</numerusform></translation> </message> <message> <location line="+4"/> @@ -1647,7 +1637,7 @@ Adres: %4 <translation>Wygenerowano</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Od</translation> @@ -1758,12 +1748,12 @@ Adres: %4 <translation>fałsz</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, nie został jeszcze pomyślnie wyemitowany</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>nieznany</translation> </message> @@ -2074,7 +2064,7 @@ Adres: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Wersja Bitcoin</translation> </message> @@ -2089,12 +2079,12 @@ Adres: %4 <translation>Wyślij polecenie do -server lub bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Lista poleceń</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Uzyskaj pomoc do polecenia</translation> </message> @@ -2169,22 +2159,47 @@ Adres: %4 <translation>Próg po którym nastąpi rozłączenie nietrzymających się zasad peerów (domyślnie: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Czas w sekundach, przez jaki nietrzymający się zasad peerzy nie będą mogli ponownie się podłączyć (domyślnie: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Odłącz bazę bloków i adresów. Zwiększa czas wyłączenia (domyślnie: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Akceptuj linię poleceń oraz polecenia JSON-RPC</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Uruchom w tle jako daemon i przyjmuj polecenia</translation> </message> @@ -2194,7 +2209,7 @@ Adres: %4 <translation>Użyj sieci testowej</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Akceptuj połączenia z zewnątrz (domyślnie: 1 jeśli nie ustawiono -proxy lub -connect)</translation> </message> @@ -2219,12 +2234,7 @@ Adres: %4 <translation>Uwaga: Sprawdź czy data i czas na Twoim komputerze są prawidłowe! Jeśli nie to Bitcoin nie będzie działał prawidłowo.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Wystąpił błąd podczas ustawiania portu RPC %i w tryb nasłuchu: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Opcje tworzenia bloku:</translation> </message> @@ -2249,17 +2259,12 @@ Adres: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Wczytywanie bloków</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Nieprawidłowy adres -tor: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Maksymalny bufor odbioru na połączenie, <n>*1000 bajtów (domyślnie: 5000)</translation> </message> @@ -2271,7 +2276,7 @@ Adres: %4 <message> <location line="+1"/> <source>Only connect to nodes in network <net> (IPv4, IPv6 or Tor)</source> - <translation type="unfinished"/> + <translation>Łącz z węzłami tylko w sieci <net> (IPv4, IPv6 lub Tor)</translation> </message> <message> <location line="+2"/> @@ -2364,27 +2369,22 @@ Adres: %4 <translation>Hasło do połączeń JSON-RPC</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Nasłuchuj połączeń JSON-RPC na <port> (domyślnie: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Przyjmuj połączenia JSON-RPC ze wskazanego adresu IP</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Wysyłaj polecenia do węzła działającego na <ip> (domyślnie: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Wykonaj polecenie kiedy najlepszy blok ulegnie zmianie (%s w komendzie zastanie zastąpione przez hash bloku)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Zaktualizuj portfel do najnowszego formatu.</translation> </message> @@ -2409,12 +2409,12 @@ Adres: %4 <translation>Głębokość weryfikacji bloku (0-6, domyślnie: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Importuj bloki z zewnętrznego pliku blk000?.dat</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Użyj OpenSSL (https) do połączeń JSON-RPC</translation> </message> @@ -2429,22 +2429,22 @@ Adres: %4 <translation>Klucz prywatny serwera (domyślnie: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Aceptowalne szyfry (domyślnie: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Ta wiadomość pomocy</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Nie można zablokować folderu danych %s. Bitcoin prawdopodobnie już działa.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2459,12 +2459,12 @@ Adres: %4 <translation>Łączy przez proxy socks</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Zezwól -addnode, -seednode i -connect na łączenie się z serwerem DNS</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Wczytywanie adresów...</translation> </message> @@ -2494,12 +2494,12 @@ Adres: %4 <translation>Błąd ładowania wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Nieprawidłowy adres -proxy: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Nieznana sieć w -onlynet: '%s'</translation> </message> @@ -2519,12 +2519,12 @@ Adres: %4 <translation>Nie można uzyskać adresu -externalip: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Nieprawidłowa kwota dla -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Błąd: nie można utworzyć węzła</translation> </message> @@ -2534,12 +2534,12 @@ Adres: %4 <translation>Błąd: Zablokowany portfel, nie można utworzyć transakcji </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Transakcja przekracza limit. Możesz wysłać ją płacąc prowizję %1, która zostaje przekazana do węzłów, które ją prześlą i pomoże wspierać sieć Bitcoin. Czy chcesz zapłacić prowizję?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Błąd: Tworzenie transakcji nie powiodło się </translation> </message> @@ -2549,12 +2549,12 @@ Adres: %4 <translation>Wysyłanie...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Błąd: transakcja została odrzucona. Może się to zdarzyć, gdy monety z Twojego portfela zostały już wydane, na przykład gdy używałeś kopii wallet.dat i bitcoiny które tam wydałeś nie zostały jeszcze odjęte z portfela z którego teraz korzystasz.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Nieprawidłowa kwota</translation> </message> @@ -2564,12 +2564,12 @@ Adres: %4 <translation>Niewystarczające środki</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Ładowanie indeksu bloku...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Dodaj węzeł do łączenia się and attempt to keep the connection open</translation> </message> @@ -2579,7 +2579,7 @@ Adres: %4 <translation>Nie można przywiązać %s na tym komputerze. Bitcoin prawdopodobnie już działa.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Znajdź peery używające IRC (domyślnie: 0)</translation> </message> @@ -2625,7 +2625,7 @@ Adres: %4 <translation>Aby użyć opcji %s</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2637,16 +2637,18 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Błąd</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> - <translation type="unfinished"/> + <translation>Musisz ustawić rpcpassword=<hasło> w pliku configuracyjnym: +%s +Jeżeli plik nie istnieje, utwórz go z uprawnieniami właściciela-tylko-do-odczytu.</translation> </message> </context> </TS>
\ No newline at end of file diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts index 62e1e18e3f..88d1f6e1b7 100644 --- a/src/qt/locale/bitcoin_pt_BR.ts +++ b/src/qt/locale/bitcoin_pt_BR.ts @@ -77,11 +77,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Assinar Mensagem</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -91,12 +96,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Excluir o endereço selecionado da lista. Apenas endereços de envio podem ser excluídos.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Excluir</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Tem certeza de que deseja criptografar sua carteira?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Carteira criptografada</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>O Bitcoin irá fechar agora para finalizar o processo de encriptação. Lembre-se de que encriptar sua carteira não protege totalmente suas bitcoins de serem roubadas por malwares que tenham infectado o seu computador.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>&Assinar Mensagem...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Sincronizando com a rede...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Visão geral</translation> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Mostrar visão geral da carteira</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transações</translation> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Editar a lista de endereços e rótulos</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Receber moedas</translation> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Mostrar a lista de endereços para receber pagamentos</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Enviar moedas</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>E&xit</translation> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Opções...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Criptografar Carteira...</translation> </message> @@ -398,42 +403,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Carregados %1 de %2 blocos do histórico de transações (%3% done).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exportar...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Enviar moedas para um endereço bitcoin</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Modificar opções de configuração para bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Exportar os dados na aba atual para um arquivo</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Criptografar ou decriptogravar carteira</translation> </message> @@ -448,7 +438,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Mudar a frase de segurança utilizada na criptografia da carteira</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -458,12 +448,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -473,7 +463,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Carteira</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Sobre o Bitcoin</translation> </message> @@ -483,12 +473,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Exibir/Ocultar</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Arquivo</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>E configurações</translation> </message> @@ -503,7 +493,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Barra de ferramentas</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Barra de ações</translation> </message> @@ -520,7 +510,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Cliente Bitcoin</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n conexão ativa na rede Bitcoin</numerusform><numerusform>%n conexões ativas na rede Bitcoin</numerusform></translation> </message> @@ -597,7 +587,7 @@ Tipo: %3 Endereço: %4</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1105,7 +1095,7 @@ Endereço: %4</translation> <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1256,7 +1246,7 @@ Endereço: %4</translation> </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation type="unfinished"/> </message> <message> @@ -1286,8 +1276,8 @@ Endereço: %4</translation> </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Send</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1642,7 +1632,7 @@ Endereço: %4</translation> <translation>Gerados</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation type="unfinished"/> @@ -1753,12 +1743,12 @@ Endereço: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, has not been successfully broadcast yet</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>unknown</translation> </message> @@ -2069,7 +2059,7 @@ Endereço: %4</translation> <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoin version</translation> </message> @@ -2085,13 +2075,13 @@ Endereço: %4</translation> </translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>List commands </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Get help for a command </translation> @@ -2173,23 +2163,48 @@ Endereço: %4</translation> <translation>Limite para desconectar peers mal comportados (padrão: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Número de segundos para impedir que peers mal comportados reconectem (padrão: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accept command line and JSON-RPC commands </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Run in the background as a daemon and accept commands </translation> @@ -2201,7 +2216,7 @@ Endereço: %4</translation> </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2226,12 +2241,7 @@ Endereço: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2256,17 +2266,12 @@ Endereço: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2373,30 +2378,24 @@ Endereço: %4</translation> </translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Listen for JSON-RPC connections on <port> (default: 8332) -</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Allow JSON-RPC connections from specified IP address </translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Send commands to node running on <ip> (default: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Atualizar carteira para o formato mais recente</translation> </message> @@ -2423,12 +2422,12 @@ Endereço: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Use OpenSSL (https) for JSON-RPC connections </translation> @@ -2446,24 +2445,24 @@ Endereço: %4</translation> </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) </translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>This help message </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2478,12 +2477,12 @@ Endereço: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> @@ -2513,12 +2512,12 @@ Endereço: %4</translation> <translation>Erro ao carregar wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2538,12 +2537,12 @@ Endereço: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2553,12 +2552,12 @@ Endereço: %4</translation> <translation>Erro: Carteira bloqueada, incapaz de criar transação </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation type="unfinished"/> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Error: Transaction creation failed </translation> </message> @@ -2568,12 +2567,12 @@ Endereço: %4</translation> <translation>Enviando...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation type="unfinished"/> </message> @@ -2583,12 +2582,12 @@ Endereço: %4</translation> <translation>Insufficient funds</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Loading block index...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation type="unfinished"/> </message> @@ -2598,7 +2597,7 @@ Endereço: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2643,7 +2642,7 @@ Endereço: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2655,12 +2654,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation type="unfinished"/> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_pt_PT.ts b/src/qt/locale/bitcoin_pt_PT.ts index 1d4a3f8711..274c14e9da 100644 --- a/src/qt/locale/bitcoin_pt_PT.ts +++ b/src/qt/locale/bitcoin_pt_PT.ts @@ -73,7 +73,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <message> <location line="+11"/> <source>Show &QR Code</source> - <translation>Mostrar &Código QR</translation> + <translation>Mostrar Código &QR</translation> </message> <message> <location line="+11"/> @@ -82,11 +82,16 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Assinar Mensagem</translation> + <source>Sign &Message</source> + <translation>Assinar &Mensagem</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>Apagar o endereço selecionado da lista</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Verifique a mensagem para assegurar que foi assinada com o endereço Bitcoin especificado</translation> </message> @@ -96,14 +101,9 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>&Verificar Mensagem</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Eliminar o endereço selecionado da lista. Apenas endereços de envio podem ser eliminados.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> - <translation>&Eliminar</translation> + <translation>E&liminar</translation> </message> <message> <location filename="../addressbookpage.cpp" line="+65"/> @@ -118,7 +118,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <message> <location line="+250"/> <source>Export Address Book Data</source> - <translation>Exportar de dados do Livro de Endereços</translation> + <translation>Exportar dados do Livro de Endereços</translation> </message> <message> <location line="+1"/> @@ -133,7 +133,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <message> <location line="+0"/> <source>Could not write to file %1.</source> - <translation>Could not write to file %1.</translation> + <translation>Não conseguiu escrever para o ficheiro %1.</translation> </message> </context> <context> @@ -232,24 +232,29 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Tem a certeza que deseja encriptar a carteira?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>IMPORTANTE: Qualquer cópia de segurança anterior da carteira deverá ser substituída com o novo, actualmente encriptado, ficheiro de carteira. Por razões de segurança, cópias de segurança não encriptadas efectuadas anteriormente do ficheiro da carteira tornar-se-ão inúteis assim que começar a usar a nova carteira encriptada.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Atenção: A tecla Caps Lock está activa!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Carteira encriptada</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>O cliente Bitcoin irá agora ser fechado para terminar o processo de encriptação. Recorde que a encriptação da sua carteira não protegerá totalmente os seus bitcoins de serem roubados por programas maliciosos que infectem o seu computador.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,19 +298,19 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Assinar &mensagem...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Sincronizando com a rede...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> - <translation>&Visão geral</translation> + <translation>Visã&o geral</translation> </message> <message> <location line="+1"/> @@ -313,7 +318,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Mostrar visão geral da carteira</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transações</translation> </message> @@ -325,7 +330,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <message> <location line="+5"/> <source>&Address Book</source> - <translation>&Livro de endereços</translation> + <translation>L&ivro de endereços</translation> </message> <message> <location line="+1"/> @@ -333,7 +338,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Editar a lista de endereços e rótulos</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Receber moedas</translation> </message> @@ -343,14 +348,14 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Mostrar a lista de endereços para receber pagamentos</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Enviar moedas</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> - <translation>S&air</translation> + <translation>Fec&har</translation> </message> <message> <location line="+1"/> @@ -378,9 +383,9 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>&Opções...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> - <translation>&Encriptar Carteira...</translation> + <translation>E&ncriptar Carteira...</translation> </message> <message> <location line="+3"/> @@ -390,7 +395,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <message> <location line="+2"/> <source>&Change Passphrase...</source> - <translation>&Mudar Palavra-passe...</translation> + <translation>Mudar &Palavra-passe...</translation> </message> <message numerus="yes"> <location line="+241"/> @@ -403,42 +408,27 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Recuperados %1 de %2 blocos do histórico de transações (%3% completo)</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exportar...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Enviar moedas para um endereço bitcoin</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Assine uma mensagem para provar que é dono de um endereço Bitcoin</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Verifique a mensagem para assegurar que foi assinada com o endereço Bitcoin especificado</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>A&ssinaturas</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Modificar opções de configuração para bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Exportar os dados no separador actual para um ficheiro</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Encriptar ou desencriptar carteira</translation> </message> @@ -453,9 +443,9 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Mudar a frase de segurança utilizada na encriptação da carteira</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> - <translation>&Janela de depuração</translation> + <translation>Janela de &depuração</translation> </message> <message> <location line="+1"/> @@ -463,12 +453,12 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Abrir consola de diagnóstico e depuração</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Verificar mensagem...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,29 +468,29 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Carteira</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Sobre o Bitcoin</translation> </message> <message> <location line="+9"/> <source>&Show / Hide</source> - <translation>&Mostrar / Ocultar</translation> + <translation>Mo&strar / Ocultar</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Ficheiro</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> - <translation>&Configurações</translation> + <translation>Con&figurações</translation> </message> <message> <location line="+6"/> <source>&Help</source> - <translation>&Ajuda</translation> + <translation>A&juda</translation> </message> <message> <location line="+9"/> @@ -508,7 +498,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Barra de separadores</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Barra de ações</translation> </message> @@ -516,7 +506,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <location line="+13"/> <location line="+9"/> <source>[testnet]</source> - <translation>[testnet]</translation> + <translation>[rede de testes]</translation> </message> <message> <location line="+0"/> @@ -525,7 +515,7 @@ Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no Open <translation>Cliente Bitcoin</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n ligação ativa à rede Bitcoin</numerusform><numerusform>%n ligações ativas à rede Bitcoin</numerusform></translation> </message> @@ -597,12 +587,13 @@ Type: %3 Address: %4 </source> <translation>Data: %1 -Quantidade: %2 +Quantia: %2 Tipo: %3 -Endereço: %4</translation> +Endereço: %4 +</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>Manuseamento URI</translation> @@ -677,7 +668,7 @@ Endereço: %4</translation> <message> <location line="+7"/> <source>&Address</source> - <translation>&Endereço</translation> + <translation>E&ndereço</translation> </message> <message> <location line="+10"/> @@ -697,7 +688,7 @@ Endereço: %4</translation> <message> <location line="+3"/> <source>Edit receiving address</source> - <translation>&Ajuda</translation> + <translation>Editar endereço de entrada</translation> </message> <message> <location line="+4"/> @@ -784,7 +775,7 @@ Endereço: %4</translation> <message> <location line="+6"/> <source>Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. Fee 0.01 recommended.</source> - <translation>Taxa de transação opcional por kB que ajuda a assegurar que as suas transações serão processadas rapidamente. A maioria das transações tem 1 kB. Taxa de 0.01 recomendada.</translation> + <translation>Taxa de transação opcional por KB que ajuda a assegurar que as suas transações serão processadas rapidamente. A maioria das transações tem 1 kB. Taxa de 0.01 recomendada.</translation> </message> <message> <location line="+15"/> @@ -804,12 +795,12 @@ Endereço: %4</translation> <message> <location line="+7"/> <source>Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached.</source> - <translation type="unfinished"/> + <translation>Destaque as bases de dados de blocos e endereços ao fechar. Isto significa que elas poderão ser movidas para outra pasta de dados, mas tornará a operação de fechar mais lenta. A carteira é sempre destacada.</translation> </message> <message> <location line="+3"/> <source>&Detach databases at shutdown</source> - <translation>&Separar bases de dados ao desligar</translation> + <translation>Separar bases de &dados ao desligar</translation> </message> <message> <location line="+21"/> @@ -834,12 +825,12 @@ Endereço: %4</translation> <message> <location line="+3"/> <source>&Connect through SOCKS proxy:</source> - <translation>&Ligar através de proxy SOCKS:</translation> + <translation>Ligar através de proxy SO&CKS:</translation> </message> <message> <location line="+9"/> <source>Proxy &IP:</source> - <translation>IP do &proxy:</translation> + <translation>&IP do proxy:</translation> </message> <message> <location line="+19"/> @@ -859,7 +850,7 @@ Endereço: %4</translation> <message> <location line="+7"/> <source>SOCKS &Version:</source> - <translation>Versão &SOCKS:</translation> + <translation>&Versão SOCKS:</translation> </message> <message> <location line="+13"/> @@ -894,12 +885,12 @@ Endereço: %4</translation> <message> <location line="+21"/> <source>&Display</source> - <translation>&Janela</translation> + <translation>&Mostrar</translation> </message> <message> <location line="+8"/> <source>User Interface &language:</source> - <translation>Linguagem da interface de &utilizador:</translation> + <translation>&Linguagem da interface de utilizador:</translation> </message> <message> <location line="+13"/> @@ -924,7 +915,7 @@ Endereço: %4</translation> <message> <location line="+3"/> <source>&Display addresses in transaction list</source> - <translation>&Mostrar endereços na lista de transações</translation> + <translation>Mostrar en&dereços na lista de transações</translation> </message> <message> <location line="+71"/> @@ -975,7 +966,7 @@ Endereço: %4</translation> <location line="+33"/> <location line="+183"/> <source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source> - <translation type="unfinished"/> + <translation>A informação mostrada poderá estar desatualizada. A sua carteira sincroniza automaticamente com a rede Bitcoin depois de estabelecer ligação, mas este processo ainda não está completo.</translation> </message> <message> <location line="-141"/> @@ -1015,7 +1006,7 @@ Endereço: %4</translation> <message> <location line="-118"/> <source>Your current balance</source> - <translation>O seu saldo actual</translation> + <translation>O seu saldo atual</translation> </message> <message> <location line="+29"/> @@ -1059,7 +1050,7 @@ Endereço: %4</translation> <message> <location line="+19"/> <source>Message:</source> - <translation>Message:</translation> + <translation>Mensagem:</translation> </message> <message> <location line="+71"/> @@ -1109,7 +1100,7 @@ Endereço: %4</translation> <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>N/D</translation> </message> @@ -1126,7 +1117,7 @@ Endereço: %4</translation> <message> <location line="+68"/> <source>Using OpenSSL version</source> - <translation>Usar versão OpenSSL</translation> + <translation>Usando versão OpenSSL</translation> </message> <message> <location line="+49"/> @@ -1186,7 +1177,7 @@ Endereço: %4</translation> <message> <location line="+3"/> <source>&Show</source> - <translation>&Mostrar</translation> + <translation>Mo&strar</translation> </message> <message> <location line="+24"/> @@ -1260,8 +1251,8 @@ Endereço: %4</translation> </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Adicionar Destinatário</translation> + <source>Add &Recipient</source> + <translation>Adicionar &Destinatário</translation> </message> <message> <location line="+20"/> @@ -1271,7 +1262,7 @@ Endereço: %4</translation> <message> <location line="+3"/> <source>Clear &All</source> - <translation>Limpar &Tudo</translation> + <translation>&Limpar Tudo</translation> </message> <message> <location line="+22"/> @@ -1290,7 +1281,7 @@ Endereço: %4</translation> </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation>&Enviar</translation> </message> <message> @@ -1346,7 +1337,7 @@ Endereço: %4</translation> <message> <location line="+5"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> - <translation>Erro: A transação foi rejeitada. Isso poderá acontecer se algumas das moedas na sua carteira já tiverem sido gastas, se por exemplo tiver usado uma cópia do ficheiro wallet.dat e as moedas foram gastas na cópia mas não foram marcadas como gastas aqui.</translation> + <translation>Erro: A transação foi rejeitada. Isso poderá acontecer se algumas das moedas na sua carteira já tiverem sido gastas, se por exemplo tiver usado uma cópia do ficheiro wallet.dat e as moedas foram gastas na cópia mas não foram marcadas como gastas aqui.</translation> </message> </context> <context> @@ -1359,12 +1350,12 @@ Endereço: %4</translation> <message> <location line="+15"/> <source>A&mount:</source> - <translation>Q&uantia:</translation> + <translation>Qu&antia:</translation> </message> <message> <location line="+13"/> <source>Pay &To:</source> - <translation>Pagar &A:</translation> + <translation>&Pagar A:</translation> </message> <message> <location line="+24"/> @@ -1375,7 +1366,7 @@ Endereço: %4</translation> <message> <location line="+9"/> <source>&Label:</source> - <translation>&Rótulo:</translation> + <translation>Rótu&lo:</translation> </message> <message> <location line="+18"/> @@ -1424,7 +1415,7 @@ Endereço: %4</translation> <location line="+13"/> <location line="+124"/> <source>&Sign Message</source> - <translation>&Assinar Mensagem</translation> + <translation>A&ssinar Mensagem</translation> </message> <message> <location line="-118"/> @@ -1434,7 +1425,7 @@ Endereço: %4</translation> <message> <location line="+18"/> <source>The address to sign the message with (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</source> - <translation>Introduza um endereço Bitcoin (p.ex. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> + <translation>O endereço a utilizar para assinar a mensagem (p.ex. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> </message> <message> <location line="+10"/> @@ -1451,7 +1442,7 @@ Endereço: %4</translation> <message> <location line="-193"/> <source>Paste address from clipboard</source> - <translation>Paste address from clipboard</translation> + <translation>Cole endereço da área de transferência</translation> </message> <message> <location line="+10"/> @@ -1493,12 +1484,12 @@ Endereço: %4</translation> <message> <location line="-64"/> <source>Enter the signing address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack.</source> - <translation type="unfinished"/> + <translation>Introduza o endereço de assinatura, mensagem (assegure-se de copiar quebras de linha, espaços, tabuladores, etc. exactamente) e assinatura abaixo para verificar a mensagem. Tenha atenção para não ler mais na assinatura do que o que estiver na mensagem assinada, para evitar ser enganado por um atacante que se encontre entre si e quem assinou a mensagem.</translation> </message> <message> <location line="+21"/> <source>The address the message was signed with (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</source> - <translation>Introduza um endereço Bitcoin (p.ex. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> + <translation>O endereço utilizado para assinar a mensagem (p.ex. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> </message> <message> <location line="+40"/> @@ -1646,7 +1637,7 @@ Endereço: %4</translation> <translation>Gerado</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>De</translation> @@ -1757,12 +1748,12 @@ Endereço: %4</translation> <translation>falso</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, ainda não foi transmitida com sucesso</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>desconhecido</translation> </message> @@ -2073,7 +2064,7 @@ Endereço: %4</translation> <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Versão Bitcoin</translation> </message> @@ -2088,12 +2079,12 @@ Endereço: %4</translation> <translation>Enviar comando para -server ou bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Listar comandos</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Obter ajuda para um comando</translation> </message> @@ -2168,22 +2159,47 @@ Endereço: %4</translation> <translation>Tolerância para desligar nós mal-formados (por defeito: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Número de segundos a impedir que nós mal-formados se liguem de novo (por defeito: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>Ocorreu um erro ao definir a porta %i do serviço RPC a escutar em IPv6, a usar IPv4: %s</translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>Ocorreu um erro ao definir a porta %u do serviço RPC a escutar em IPv4: %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Separar bases de dados de blocos e endereços. Aumenta o tempo necessário para desligar (por defeito: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>Escutar por ligações JSON-RPC em <port> (por defeito: 8332 ou rede de testes: 18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Aceitar comandos da consola e JSON-RPC</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>Importar ficheiro de dados da cadeia de blocos.</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>Importar ficheiro de início rápido da cadeia de blocos.</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Correr o processo como um daemon e aceitar comandos</translation> </message> @@ -2193,14 +2209,14 @@ Endereço: %4</translation> <translation>Utilizar a rede de testes - testnet</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Aceitar ligações externas (padrão: 1 sem -proxy ou -connect)</translation> </message> <message> <location line="-20"/> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)</source> - <translation type="unfinished"/> + <translation>Definir tamanho máximo de transações de alta-/baixa-prioridade em bytes (por defeito: 27000)</translation> </message> <message> <location line="+5"/> @@ -2210,7 +2226,7 @@ Endereço: %4</translation> <message> <location line="+3"/> <source>Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.</source> - <translation type="unfinished"/> + <translation>Atenção: As transações mostradas poderão não estar correctas! Poderá ter que atualizar ou outros nós poderão ter que atualizar.</translation> </message> <message> <location line="+3"/> @@ -2218,12 +2234,7 @@ Endereço: %4</translation> <translation>Atenção: Por favor verifique que a data e hora do seu computador estão correctas! Se o seu relógio não estiver certo o Bitcoin não irá funcionar correctamente.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Ocorreu um erro ao definir a porta de escuta %i do serviço RPC: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Opções de criação de Bloco:</translation> </message> @@ -2248,17 +2259,12 @@ Endereço: %4</translation> <translation>Encontrar pares usando procura DNS (por defeito: 1 excepto -connect)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>A importar blocos...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Endereço -tor inválido: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Armazenamento intermédio de recepção por ligação, <n>*1000 bytes (por defeito: 5000)</translation> </message> @@ -2275,7 +2281,7 @@ Endereço: %4</translation> <message> <location line="+2"/> <source>Output extra debugging information. Implies all other -debug* options</source> - <translation type="unfinished"/> + <translation>Produzir informação de depuração extra. Implica todas as outras opções -debug*</translation> </message> <message> <location line="+1"/> @@ -2310,17 +2316,17 @@ Endereço: %4</translation> <message> <location line="+7"/> <source>Set maximum block size in bytes (default: 250000)</source> - <translation type="unfinished"/> + <translation>Definir tamanho máximo de um bloco em bytes (por defeito: 250000)</translation> </message> <message> <location line="+1"/> <source>Set minimum block size in bytes (default: 0)</source> - <translation type="unfinished"/> + <translation>Definir tamanho minímo de um bloco em bytes (por defeito: 0)</translation> </message> <message> <location line="+1"/> <source>Shrink debug.log file on client startup (default: 1 when no -debug)</source> - <translation type="unfinished"/> + <translation>Encolher ficheiro debug.log ao iniciar o cliente (por defeito: 1 sem -debug definido)</translation> </message> <message> <location line="+2"/> @@ -2340,7 +2346,7 @@ Endereço: %4</translation> <message> <location line="+1"/> <source>Use proxy to reach tor hidden services (default: same as -proxy)</source> - <translation type="unfinished"/> + <translation>Utilizar proxy para aceder a serviços escondidos Tor (por defeito: mesmo que -proxy)</translation> </message> <message> <location line="+2"/> @@ -2363,27 +2369,22 @@ Endereço: %4</translation> <translation>Palavra-passe para ligações JSON-RPC</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Escutar por ligações JSON-RPC na porta <port> (por defeito: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Permitir ligações JSON-RPC do endereço IP especificado</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Enviar comandos para o nó a correr em <ip> (por defeito: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Executar comando quando mudar o melhor bloco (na consola, %s é substituído pela hash do bloco)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Atualize a carteira para o formato mais recente</translation> </message> @@ -2408,12 +2409,12 @@ Endereço: %4</translation> <translation>Minuciosidade da verificação de blocos é (0-6, por defeito: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Importar blocos de ficheiro blk000?.dat externo</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Usar OpenSSL (https) para ligações JSON-RPC</translation> </message> @@ -2428,22 +2429,22 @@ Endereço: %4</translation> <translation>Chave privada do servidor (por defeito: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Cifras aceitáveis (por defeito: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Esta mensagem de ajuda</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Impossível trancar a pasta de dados %s. Provavelmente o Bitcoin já está a ser executado.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2458,14 +2459,14 @@ Endereço: %4</translation> <translation>Ligar através de um proxy socks</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Permitir procuras DNS para -addnode, -seednode e -connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> - <translation>A carregar endereços...</translation> + <translation>Carregar endereços...</translation> </message> <message> <location line="-26"/> @@ -2493,12 +2494,12 @@ Endereço: %4</translation> <translation>Erro ao carregar wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Endereço -proxy inválido: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Rede desconhecida especificada em -onlynet: '%s'</translation> </message> @@ -2518,12 +2519,12 @@ Endereço: %4</translation> <translation>Não conseguiu resolver endereço -externalip: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Quantia inválida para -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Erro: não iniciou o nó</translation> </message> @@ -2533,14 +2534,14 @@ Endereço: %4</translation> <translation>Erro: Carteira bloqueada, incapaz de criar transação </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Erro: Esta transação requer uma taxa de transação mínima de %s devido á sua quantia, complexidade, ou uso de fundos recebidos recentemente </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> - <translation>Error: Transaction creation failed </translation> + <translation>Erro: Criação da transação falhou</translation> </message> <message> <location line="+42"/> @@ -2548,27 +2549,27 @@ Endereço: %4</translation> <translation>Enviando...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> - <translation>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</translation> + <translation>Erro: A transação foi rejeitada. Isso poderá acontecer se algumas das moedas na sua carteira já tiverem sido gastas, se por exemplo tiver usado uma cópia do ficheiro wallet.dat e as moedas foram gastas na cópia mas não foram marcadas como gastas aqui.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Quantia inválida</translation> </message> <message> <location line="-4"/> <source>Insufficient funds</source> - <translation>Insufficient funds</translation> + <translation>Fundos insuficientes</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> - <translation>A carregar índice de blocos...</translation> + <translation>Carregar índice de blocos...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Adicione um nó ao qual se ligar e tentar manter a ligação aberta</translation> </message> @@ -2578,19 +2579,19 @@ Endereço: %4</translation> <translation>Incapaz de vincular à porta %s neste computador. Provavelmente o Bitcoin já está a funcionar.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Encontrar pares usando IRC (por defeito: 0)</translation> </message> <message> <location line="-2"/> <source>Fee per KB to add to transactions you send</source> - <translation>Fee per KB to add to transactions you send</translation> + <translation>Taxa por KB a adicionar a transações enviadas</translation> </message> <message> <location line="+19"/> <source>Loading wallet...</source> - <translation>A carregar carteira...</translation> + <translation>Carregar carteira...</translation> </message> <message> <location line="-39"/> @@ -2623,7 +2624,7 @@ Endereço: %4</translation> <translation>Para usar a opção %s</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2642,12 +2643,12 @@ Se o ficheiro não existir, crie-o com permissões de leitura apenas para o dono </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Erro</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_ro_RO.ts b/src/qt/locale/bitcoin_ro_RO.ts index efe3d6088c..85afc6dd52 100644 --- a/src/qt/locale/bitcoin_ro_RO.ts +++ b/src/qt/locale/bitcoin_ro_RO.ts @@ -77,11 +77,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -91,12 +96,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Ștergeți adresa selectată din listă. Doar adresele de trimitere pot fi șterse.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Șterge</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Sunteţi sigur că doriţi să criptaţi portofelul electronic?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Portofel criptat </translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation type="unfinished"/> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Se sincronizează cu reţeaua...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Detalii</translation> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Afişează detalii despre portofelul electronic</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Tranzacţii</translation> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Editaţi lista de adrese şi etichete.</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Primiţi Bitcoin</translation> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Lista de adrese pentru recepţionarea plăţilor</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Trimiteţi Bitcoin</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation type="unfinished"/> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Setări...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>Criptează portofelul electronic...</translation> </message> @@ -398,42 +403,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>S-au descărcat %1 din %2 blocuri din istoricul tranzaciilor (%3% done).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exportă...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>&Trimiteţi Bitcoin către o anumită adresă</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Modifică setările pentru Bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Criptează şi decriptează portofelul electronic</translation> </message> @@ -448,7 +438,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Schimbă parola folosită pentru criptarea portofelului electronic</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -458,12 +448,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -473,7 +463,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Portofelul</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Despre Bitcoin</translation> </message> @@ -483,12 +473,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Fişier</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Setări</translation> </message> @@ -503,7 +493,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Bara de ferestre de lucru</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Bara de acţiuni</translation> </message> @@ -520,7 +510,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n active connections to Bitcoin network</numerusform><numerusform>%n active connections to Bitcoin network</numerusform><numerusform>%n active connections to Bitcoin network</numerusform></translation> </message> @@ -594,7 +584,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1101,7 +1091,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1252,8 +1242,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Adaugă destinatar</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1282,8 +1272,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&S Trimite</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1638,7 +1628,7 @@ Address: %4 <translation>Generat</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>De la</translation> @@ -1749,12 +1739,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, nu s-a propagat încă</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>necunoscut</translation> </message> @@ -2065,7 +2055,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>versiunea Bitcoin</translation> </message> @@ -2080,12 +2070,12 @@ Address: %4 <translation>Trimite comanda la -server sau bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Listă de comenzi</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Ajutor pentru o comandă</translation> </message> @@ -2160,22 +2150,47 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation type="unfinished"/> </message> @@ -2185,7 +2200,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2210,12 +2225,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2240,17 +2250,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2355,27 +2360,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation type="unfinished"/> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation type="unfinished"/> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2400,12 +2400,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation type="unfinished"/> </message> @@ -2420,22 +2420,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation type="unfinished"/> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation type="unfinished"/> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation type="unfinished"/> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2450,12 +2450,12 @@ Address: %4 <translation>Conectează prin proxy SOCKS</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Încarc adrese...</translation> </message> @@ -2485,12 +2485,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2510,12 +2510,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2525,12 +2525,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Această tranzacţie depăşeşte limita. Puteţi iniţia tranzacţia platind un comision de %1, de care vor beneficia nodurile care procesează tranzacţia şi ajută la menţinerea reţelei. Acceptaţi plata comisionului?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Eroare: Tranyacţia nu a putut fi iniţiată </translation> </message> @@ -2540,12 +2540,12 @@ Address: %4 <translation>Transmitere...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Eroare: Tranyacţia a fost respinsă. Acesta poate fi rezultatul cheltuirii prealabile a unei sume de bitcoin din portofelul electronic, ca în cazul folosirii unei copii a fisierului wallet.dat, în care s-au efectuat tranzacţii neînregistrate în fisierul curent.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation type="unfinished"/> </message> @@ -2555,12 +2555,12 @@ Address: %4 <translation>Fonduri insuficiente</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Încarc indice bloc...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation type="unfinished"/> </message> @@ -2570,7 +2570,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2615,7 +2615,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2627,12 +2627,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Eroare</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index 0225851bd4..d3b2634a1e 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -82,11 +82,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation>&Подписать сообщение</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>Удалить выбранный адрес из списка</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Проверить сообщение, чтобы убедиться, что оно было подписано указанным адресом Bitcoin</translation> </message> @@ -96,12 +101,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Проверить сообщение</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Удалить выделенный адрес из списка (могут быть удалены только записи из адресной книги).</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Удалить</translation> </message> @@ -232,24 +232,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Вы уверены, что хотите зашифровать ваш бумажник?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>ВАЖНО: все предыдущие резервные копии вашего кошелька должны быть заменены новым зашифрованным файлом. В целях безопасности предыдущие резервные копии нешифрованного кошелька станут бесполезны, как только вы начнёте использовать новый шифрованный кошелёк.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Внимание: Caps Lock включен!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Бумажник зашифрован</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Сейчас программа закроется для завершения процесса шифрования. Помните, что шифрование вашего бумажника не может полностью защитить ваши биткоины от кражи с помощью инфицирования вашего компьютера вредоносным ПО.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>&Подписать сообщение</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Синхронизация с сетью...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>О&бзор</translation> </message> @@ -313,7 +318,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Показать общий обзор действий с бумажником</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Транзакции</translation> </message> @@ -333,7 +338,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Изменить список сохранённых адресов и меток к ним</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Получение монет</translation> </message> @@ -343,12 +348,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Показать список адресов для получения платежей</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>Отп&равка монет</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>В&ыход</translation> </message> @@ -378,7 +383,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Оп&ции...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Зашифровать бумажник</translation> </message> @@ -403,42 +408,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Загружено %1 из %2 блоков истории операций (%3% завершено).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Экспорт...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Отправить монеты на указанный адрес Bitcoin</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Подписать сообщение, чтобы доказать владение адресом Bitcoin</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Проверить сообщение, чтобы убедиться, что оно было подписано указанным адресом Bitcoin</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>&Подписи</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Изменить параметры конфигурации Bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Экспортировать данные из вкладки в файл</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Зашифровать или расшифровать бумажник</translation> </message> @@ -453,7 +443,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Изменить пароль шифрования бумажника</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Окно отладки</translation> </message> @@ -463,12 +453,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Открыть консоль отладки и диагностики</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Проверить сообщение...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Биткоин</translation> </message> @@ -478,7 +468,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Бумажник</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&О Bitcoin</translation> </message> @@ -488,12 +478,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Показать / Скрыть</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Файл</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Настройки</translation> </message> @@ -508,7 +498,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Панель вкладок</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Панель действий</translation> </message> @@ -525,7 +515,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Bitcoin клиент</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n активное соединение с сетью</numerusform><numerusform>%n активных соединений с сетью</numerusform><numerusform>%n активных соединений с сетью</numerusform></translation> </message> @@ -603,7 +593,7 @@ Address: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>Обработка URI</translation> @@ -1110,7 +1100,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>Н/Д</translation> </message> @@ -1261,7 +1251,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation>&Добавить получателя</translation> </message> <message> @@ -1291,7 +1281,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation>&Отправить</translation> </message> <message> @@ -1647,7 +1637,7 @@ Address: %4 <translation>Сгенерированно</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>От</translation> @@ -1758,12 +1748,12 @@ Address: %4 <translation>ложь</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, ещё не было успешно разослано</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>неизвестно</translation> </message> @@ -2074,7 +2064,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Версия</translation> </message> @@ -2089,13 +2079,13 @@ Address: %4 <translation>Отправить команду на -server или bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Список команд </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Получить помощь по команде</translation> </message> @@ -2170,22 +2160,47 @@ Address: %4 <translation>Порог для отключения неправильно ведущих себя узлов (по умолчанию: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Число секунд блокирования неправильно ведущих себя узлов (по умолчанию: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>Произошла ошибка при открытии на прослушивание IPv6 RCP-порта %i, возвращаемся к IPv4: %s</translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>Произошла ошибка при открытии RPC-порта %u для прослушивания на IPv4: %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Отключить базы данных блоков и адресов. Увеличивает время завершения работы (по умолчанию: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>Прослушивать подключения JSON-RPC на <порту> (по умолчанию: 8332 или для testnet: 18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Принимать командную строку и команды JSON-RPC</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>Импортируется файл цепи блоков.</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>Импортируется bootstrap-файл цепи блоков.</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Запускаться в фоне как демон и принимать команды</translation> </message> @@ -2195,7 +2210,7 @@ Address: %4 <translation>Использовать тестовую сеть</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Принимать подключения извне (по умолчанию: 1, если не используется -proxy или -connect)</translation> </message> @@ -2220,12 +2235,7 @@ Address: %4 <translation>Внимание: убедитесь, что дата и время на Вашем компьютере выставлены верно. Если Ваши часы идут неправильно, Bitcoin будет работать некорректно.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Произошла ошибка при открытии RPC-порта %i для прослушивания: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Параметры создания блоков:</translation> </message> @@ -2250,17 +2260,12 @@ Address: %4 <translation>Искать узлы с помощью DNS (по умолчанию: 1, если не указан -connect)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Импортирование блоков...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Неверный адрес -tor: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Максимальный размер буфера приёма на соединение, <n>*1000 байт (по умолчанию: 5000)</translation> </message> @@ -2366,27 +2371,22 @@ Address: %4 <translation>Пароль для подключений JSON-RPC</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Ожидать подключения JSON-RPC на <порт> (по умолчанию: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Разрешить подключения JSON-RPC с указанного IP</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Посылать команды узлу, запущенному на <ip> (по умолчанию: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Выполнить команду, когда появляется новый блок (%s в команде заменяется на хэш блока)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Обновить бумажник до последнего формата</translation> </message> @@ -2411,12 +2411,12 @@ Address: %4 <translation>Насколько тщательно проверять блоки (0-6, по умолчанию: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Импортировать блоки из внешнего файла blk000?.dat</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Использовать OpenSSL (https) для подключений JSON-RPC</translation> </message> @@ -2431,22 +2431,22 @@ Address: %4 <translation>Приватный ключ сервера (по умолчанию: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Разрешённые алгоритмы (по умолчанию: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Эта справка</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Невозможно установить блокировку на рабочую директорию %s. Возможно, бумажник уже запущен.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Биткоин</translation> </message> @@ -2461,12 +2461,12 @@ Address: %4 <translation>Подключаться через socks прокси</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Разрешить поиск в DNS для -addnode, -seednode и -connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Загрузка адресов...</translation> </message> @@ -2496,12 +2496,12 @@ Address: %4 <translation>Ошибка при загрузке wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Неверный адрес -proxy: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>В параметре -onlynet указана неизвестная сеть: '%s'</translation> </message> @@ -2521,12 +2521,12 @@ Address: %4 <translation>Не удаётся разрешить адрес в параметре -externalip: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Неверное количество в параметре -paytxfee=<кол-во>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Ошибка: не удалось запустить узел</translation> </message> @@ -2536,12 +2536,12 @@ Address: %4 <translation>Ошибка: бумажник заблокирован, невозможно создать транзакцию </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Ошибка: эта транзакция требует комиссию в размере как минимум %s из-за её объёма, сложности или использования недавно полученных средств </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Ошибка: Создание транзакции не удалось </translation> </message> @@ -2551,12 +2551,12 @@ Address: %4 <translation>Отправка...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Ошибка: В транзакции отказано. Такое может произойти, если некоторые монеты уже были потрачены, например, если Вы используете одну копию файла wallet.dat, а монеты были потрачены из другой копии, но не были отмечены как потраченные в этой.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Неверное количество</translation> </message> @@ -2566,12 +2566,12 @@ Address: %4 <translation>Недостаточно монет</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Загрузка индекса блоков...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Добавить узел для подключения и пытаться поддерживать соединение открытым</translation> </message> @@ -2581,7 +2581,7 @@ Address: %4 <translation>Невозможно привязаться к %s на этом компьютере. Возможно, Bitcoin уже работает.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Найти участников через IRC (по умолчанию: 0)</translation> </message> @@ -2626,7 +2626,7 @@ Address: %4 <translation>Чтобы использовать опцию %s</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2645,12 +2645,12 @@ rpcpassword=%s </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Ошибка</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts index 9c62a91354..80e00c248d 100644 --- a/src/qt/locale/bitcoin_sk.ts +++ b/src/qt/locale/bitcoin_sk.ts @@ -77,11 +77,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Podpísať Správu</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -91,12 +96,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Zmazať práve zvolená adresu zo zoznamu. Len adresy pre odosielanie sa dajú zmazať.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Zmazať</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Ste si istí, že si želáte zašifrovať peňaženku?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Varovanie: Caps Lock je zapnutý</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Peňaženka zašifrovaná</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Bitcoin sa teraz ukončí pre dokončenie procesu šifrovania. Pamätaj že šifrovanie peňaženky Ťa nemôže úplne ochrániť pred kráďežou bitcoinov pomocou škodlivého software.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>Podpísať &správu...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synchronizácia so sieťou...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Prehľad</translation> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Zobraziť celkový prehľad o peňaženke</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transakcie</translation> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Editovať zoznam uložených adries a popisov</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Prijať bitcoins</translation> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Zobraziť zoznam adries pre prijímanie platieb.</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Poslať bitcoins</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>U&končiť</translation> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Možnosti...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Zašifrovať Peňaženku...</translation> </message> @@ -398,42 +403,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Stiahnutých %1 (of %2) blokov transakčnej histórie (%3% done).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Export...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Poslať bitcoins na adresu</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Podpísať správu a dokázať že vlastníte túto adresu</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Upraviť možnosti nastavenia pre bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Exportovať tento náhľad do súboru</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Zašifrovať alebo dešifrovať peňaženku</translation> </message> @@ -448,7 +438,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Zmeniť heslo použité na šifrovanie peňaženky</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -458,12 +448,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -473,7 +463,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Peňaženka</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&O Bitcoin</translation> </message> @@ -483,12 +473,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Súbor</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Nastavenia</translation> </message> @@ -503,7 +493,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Lišta záložiek</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Lišta aktvivít</translation> </message> @@ -520,7 +510,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktívne spojenie v Bitcoin sieti</numerusform><numerusform>%n aktívne spojenia v Bitcoin sieti</numerusform><numerusform>%n aktívnych spojení v Bitconi sieti</numerusform></translation> </message> @@ -597,7 +587,7 @@ Typ: %3 Adresa: %4</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1104,7 +1094,7 @@ Adresa: %4</translation> <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1255,8 +1245,8 @@ Adresa: %4</translation> </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Pridať príjemcu</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1285,8 +1275,8 @@ Adresa: %4</translation> </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Odoslať</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1641,7 +1631,7 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>od</translation> @@ -1752,12 +1742,12 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, ešte nebola úspešne odoslaná</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>neznámy</translation> </message> @@ -2068,7 +2058,7 @@ Adresa: %4</translation> <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoin verzia</translation> </message> @@ -2083,12 +2073,12 @@ Adresa: %4</translation> <translation>Odoslať príkaz -server alebo bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Zoznam príkazov</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Dostať pomoc pre príkaz</translation> </message> @@ -2163,22 +2153,47 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Prijímať príkazy z príkazového riadku a JSON-RPC</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Bežať na pozadí ako démon a prijímať príkazy</translation> </message> @@ -2188,7 +2203,7 @@ Adresa: %4</translation> <translation>Použiť testovaciu sieť</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2213,12 +2228,7 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2243,17 +2253,12 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Neplatná adresa tor: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2358,27 +2363,22 @@ Adresa: %4</translation> <translation>Heslo pre JSON-rPC spojenia</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Počúvať JSON-RPC spojeniam na <port> (predvolené: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Povoliť JSON-RPC spojenia z určenej IP adresy.</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Poslať príkaz nóde bežiacej na <ip> (predvolené: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2403,12 +2403,12 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Použiť OpenSSL (https) pre JSON-RPC spojenia</translation> </message> @@ -2423,22 +2423,22 @@ Adresa: %4</translation> <translation>Súkromný kľúč servra (predvolené: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Prijateľné šifry (predvolené: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Táto pomocná správa</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation type="unfinished"/> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -2453,12 +2453,12 @@ Adresa: %4</translation> <translation>Pripojenie cez socks proxy</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Povoliť vyhľadávanie DNS pre pridanie nódy a spojenie</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Načítavanie adries...</translation> </message> @@ -2488,12 +2488,12 @@ Adresa: %4</translation> <translation>Chyba načítania wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Neplatná adresa proxy: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2513,12 +2513,12 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Neplatná suma pre -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2528,12 +2528,12 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Veľkosť tejto transakcie prekračuje limit. Stále ju však môžete odoslať za poplatok %1 ktorý bude pripísaný uzlu spracúvajúcemu vašu transakciu. Chcete zaplatiť poplatok?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Chyba: Zlyhalo vytvorenie transakcie</translation> </message> @@ -2543,12 +2543,12 @@ Adresa: %4</translation> <translation>Odosielanie...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Chyba: Transakcia bola odmietnutá. Toto sa môže stať ak niektoré z mincí vo vašej peňaženke boli už utratené, napríklad ak používaš kópiu wallet.dat a mince označené v druhej kópií neboli označené ako utratené v tejto.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Neplatná suma</translation> </message> @@ -2558,12 +2558,12 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Načítavanie zoznamu blokov...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Pridať nódu a pripojiť sa and attempt to keep the connection open</translation> </message> @@ -2573,7 +2573,7 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2618,7 +2618,7 @@ Adresa: %4</translation> <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2630,12 +2630,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Chyba</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_sr.ts b/src/qt/locale/bitcoin_sr.ts index f2e3e4da4c..bff05a4195 100644 --- a/src/qt/locale/bitcoin_sr.ts +++ b/src/qt/locale/bitcoin_sr.ts @@ -77,11 +77,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -91,12 +96,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Брише изабрану адресу. Могуће је брисати само адресе са којих се шаље.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Избриши</translation> </message> @@ -227,24 +227,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Да ли сте сигурни да желите да се новчаник шифује?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation type="unfinished"/> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Новчаник је шифрован</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation type="unfinished"/> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -288,17 +293,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation type="unfinished"/> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Синхронизација са мрежом у току...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Општи преглед</translation> </message> @@ -308,7 +313,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Погледајте општи преглед новчаника</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Трансакције</translation> </message> @@ -328,7 +333,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Уредите запамћене адресе и њихове етикете</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>П&римање новца</translation> </message> @@ -338,12 +343,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Прегледајте листу адреса на којима прихватате уплате</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Слање новца</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation type="unfinished"/> </message> @@ -373,7 +378,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>П&оставке...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Шифровање новчаника...</translation> </message> @@ -398,42 +403,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Преузето је %1 од укупно %2 блокова историјата трансакција (%3% done).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Извоз...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Пошаљите новац на bitcoin адресу</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Изаберите могућности bitcoin-а</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Шифровање и дешифровање новчаника</translation> </message> @@ -448,7 +438,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Мењање лозинке којом се шифрује новчаник</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -458,12 +448,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -473,7 +463,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>новчаник</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&О Bitcoin-у</translation> </message> @@ -483,12 +473,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Фајл</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Подешавања</translation> </message> @@ -503,7 +493,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Трака са картицама</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Трака са алаткама</translation> </message> @@ -520,7 +510,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n активна веза са Bitcoin мрежом</numerusform><numerusform>%n активне везе са Bitcoin мрежом</numerusform><numerusform>%n активних веза са Bitcoin мрежом</numerusform></translation> </message> @@ -594,7 +584,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1101,7 +1091,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1252,7 +1242,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation type="unfinished"/> </message> <message> @@ -1282,8 +1272,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Пошаљи</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1638,7 +1628,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation type="unfinished"/> @@ -1749,12 +1739,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation type="unfinished"/> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation type="unfinished"/> </message> @@ -2065,7 +2055,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoin верзија</translation> </message> @@ -2080,12 +2070,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation type="unfinished"/> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation type="unfinished"/> </message> @@ -2160,22 +2150,47 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation type="unfinished"/> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation type="unfinished"/> </message> @@ -2185,7 +2200,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2210,12 +2225,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2240,17 +2250,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation type="unfinished"/> </message> @@ -2355,27 +2360,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation type="unfinished"/> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation type="unfinished"/> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation type="unfinished"/> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2400,12 +2400,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation type="unfinished"/> </message> @@ -2420,22 +2420,22 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation type="unfinished"/> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation type="unfinished"/> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation type="unfinished"/> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation type="unfinished"/> </message> @@ -2450,12 +2450,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation type="unfinished"/> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation type="unfinished"/> </message> @@ -2485,12 +2485,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2510,12 +2510,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2525,12 +2525,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation type="unfinished"/> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation type="unfinished"/> </message> @@ -2540,12 +2540,12 @@ Address: %4 <translation>Слање у току...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation type="unfinished"/> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation type="unfinished"/> </message> @@ -2555,12 +2555,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation type="unfinished"/> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation type="unfinished"/> </message> @@ -2570,7 +2570,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2615,7 +2615,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2627,12 +2627,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation type="unfinished"/> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts index f7139a814e..bb779e08e6 100644 --- a/src/qt/locale/bitcoin_sv.ts +++ b/src/qt/locale/bitcoin_sv.ts @@ -83,11 +83,16 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Signera meddelande</translation> + <source>Sign &Message</source> + <translation>Signera &Meddelande</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>Ta bort den valda adressen från listan</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Verifiera meddelandet för att vara säker på att den var signerad med den specificerade Bitcoin-adressen</translation> </message> @@ -97,12 +102,7 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>&Verifiera Meddelande</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Ta bort den valda adressen från listan. Bara avsändar-adresser kan tas bort.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Ta bort</translation> </message> @@ -233,24 +233,29 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Är du säker på att du vill kryptera din plånbok?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>VIKTIGT: Alla tidigare säkerhetskopior du har gjort av plånbokens fil ska ersättas med den nya genererade, krypterade plånboks filen. Av säkerhetsskäl kommer tidigare säkerhetskopior av den okrypterade plånboks filen blir oanvändbara när du börjar använda en ny, krypterad plånbok.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Varning: Caps Lock är påslaget!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Plånboken är krypterad</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Programmet kommer nu att stänga ner för att färdigställa krypteringen. Tänk på att en krypterad plånbok inte skyddar mot stöld om din dator är infekterad med en keylogger.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -294,17 +299,17 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> - <translation>Signera &meddelande...</translation> + <translation>Signera &Meddelande...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Synkroniserar med nätverk...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Översikt</translation> </message> @@ -314,7 +319,7 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Visa översiktsvy av plånbok</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Transaktioner</translation> </message> @@ -334,7 +339,7 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Redigera listan med lagrade adresser och etiketter</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&Ta emot bitcoins</translation> </message> @@ -344,12 +349,12 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Visa listan med adresser för att ta emot betalningar</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&Skicka bitcoins</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Avsluta</translation> </message> @@ -379,7 +384,7 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>&Alternativ...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Kryptera plånbok...</translation> </message> @@ -404,42 +409,27 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Laddat ner %1 av %2 block från transaktionshistoriken (%3% klart).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Exportera...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Skicka mynt till en Bitcoin-adress</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Signera ett meddelande för att bevisa att du äger denna adress</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Verifiera meddelandet för att vara säker på att den var signerad med den specificerade Bitcoin-adressen</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>S&ignaturer</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Ändra konfigurationsalternativ för Bitcoin</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Exportera informationen i den nuvarande fliken till en fil</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Kryptera eller dekryptera plånbok</translation> </message> @@ -454,7 +444,7 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Byt lösenord för kryptering av plånbok</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Debug fönster</translation> </message> @@ -464,12 +454,12 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Öppna debug- och diagnostikkonsolen</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&Verifiera meddelande...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -479,7 +469,7 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Plånbok</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Om Bitcoin</translation> </message> @@ -489,12 +479,12 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>&Visa / Göm</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Arkiv</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Inställningar</translation> </message> @@ -509,7 +499,7 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Verktygsfält för Tabbar</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Verktygsfältet för Handlingar</translation> </message> @@ -526,7 +516,7 @@ Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användni <translation>Bitcoin-klient</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktiv anslutning till Bitcoin-nätverket</numerusform><numerusform>%n aktiva anslutningar till Bitcoin-nätverket</numerusform></translation> </message> @@ -604,7 +594,7 @@ Adress: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>URI hantering</translation> @@ -758,7 +748,7 @@ Adress: %4 <message> <location line="+1"/> <source>Set language, for example "de_DE" (default: system locale)</source> - <translation>Ändra språk, till exempel "de_DE" (standard: systemets språk)</translation> + <translation>Ändra språk, till exempel "de_DE" (förvalt: systemets språk)</translation> </message> <message> <location line="+1"/> @@ -768,7 +758,7 @@ Adress: %4 <message> <location line="+1"/> <source>Show splash screen on startup (default: 1)</source> - <translation>Visa startbilden vid uppstart (standard: 1)</translation> + <translation>Visa startbilden vid uppstart (förvalt: 1)</translation> </message> </context> <context> @@ -1111,7 +1101,7 @@ Adress: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>ej tillgänglig</translation> </message> @@ -1262,8 +1252,8 @@ Adress: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&Lägg till mottagare</translation> + <source>Add &Recipient</source> + <translation>Lägg till &mottagare</translation> </message> <message> <location line="+20"/> @@ -1292,7 +1282,7 @@ Adress: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation>&Skicka</translation> </message> <message> @@ -1420,13 +1410,13 @@ Adress: %4 <message> <location filename="../forms/signverifymessagedialog.ui" line="+14"/> <source>Signatures - Sign / Verify a Message</source> - <translation>Signaturer - Signera / Verifiera ett meddelande</translation> + <translation>Signaturer - Signera / Verifiera ett Meddelande</translation> </message> <message> <location line="+13"/> <location line="+124"/> <source>&Sign Message</source> - <translation>&Signera meddelande</translation> + <translation>&Signera Meddelande</translation> </message> <message> <location line="-118"/> @@ -1495,7 +1485,7 @@ Adress: %4 <message> <location line="-64"/> <source>Enter the signing address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack.</source> - <translation>Skriv in din adress, meddelande (se till att du kopierar radbrytningar, mellanslag, tabbar osv exakt) och signatur nedan för att verifiera meddelandet. Var noga med att inte läsa in mer i signaturen än vad som finns i det signerade meddelandet, för att undvika att luras av en man-in-the-middle attack.</translation> + <translation>Skriv in din adress, meddelande (se till att du kopierar radbrytningar, mellanslag, tabbar, osv. exakt) och signatur nedan för att verifiera meddelandet. Var noga med att inte läsa in mer i signaturen än vad som finns i det signerade meddelandet, för att undvika att luras av en man-in-the-middle attack.</translation> </message> <message> <location line="+21"/> @@ -1648,7 +1638,7 @@ Adress: %4 <translation>Genererad</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Från</translation> @@ -1759,12 +1749,12 @@ Adress: %4 <translation>falsk</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, har inte lyckats skickas ännu</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>okänd</translation> </message> @@ -2075,7 +2065,7 @@ Adress: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoin version</translation> </message> @@ -2090,12 +2080,12 @@ Adress: %4 <translation>Skicka kommando till -server eller bitcoind</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Lista kommandon</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Få hjälp med ett kommando</translation> </message> @@ -2107,12 +2097,12 @@ Adress: %4 <message> <location line="+23"/> <source>Specify configuration file (default: bitcoin.conf)</source> - <translation>Ange konfigurationsfil (standard: bitcoin.conf)</translation> + <translation>Ange konfigurationsfil (förvalt: bitcoin.conf)</translation> </message> <message> <location line="+3"/> <source>Specify pid file (default: bitcoind.pid)</source> - <translation>Ange pid fil (standard: bitcoind.pid)</translation> + <translation>Ange pid fil (förvalt: bitcoind.pid)</translation> </message> <message> <location line="-47"/> @@ -2132,22 +2122,22 @@ Adress: %4 <message> <location line="-8"/> <source>Set database cache size in megabytes (default: 25)</source> - <translation>Sätt databas cache storleken i megabyte (standard: 25)</translation> + <translation>Sätt databas cache storleken i megabyte (förvalt: 25)</translation> </message> <message> <location line="+1"/> <source>Set database disk log size in megabytes (default: 100)</source> - <translation>Sätt databasens loggfil storlek i megabyte (standard: 100)</translation> + <translation>Sätt databasens loggfil storlek i megabyte (förvalt: 100)</translation> </message> <message> <location line="-26"/> <source>Listen for connections on <port> (default: 8333 or testnet: 18333)</source> - <translation>Lyssna efter anslutningar på <port> (förval: 8333 eller testnet: 18333)</translation> + <translation>Lyssna efter anslutningar på <port> (förvalt: 8333 eller testnet: 18333)</translation> </message> <message> <location line="+4"/> <source>Maintain at most <n> connections to peers (default: 125)</source> - <translation>Ha som mest <n> anslutningar till andra klienter (förval: 125)</translation> + <translation>Ha som mest <n> anslutningar till andra klienter (förvalt 125)</translation> </message> <message> <location line="-33"/> @@ -2167,25 +2157,50 @@ Adress: %4 <message> <location line="+77"/> <source>Threshold for disconnecting misbehaving peers (default: 100)</source> - <translation>Tröskelvärde för att koppla ifrån klienter som missköter sig (förval: 100)</translation> + <translation>Tröskelvärde för att koppla ifrån klienter som missköter sig (förvalt: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> - <translation>Antal sekunder att hindra klienter som missköter sig från att ansluta (förval: 86400)</translation> + <translation>Antal sekunder att hindra klienter som missköter sig från att ansluta (förvalt: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>Ett fel uppstod vid upprättandet av RPC port %i för att lyssna på IPv6, faller tillbaka till IPV4: %s</translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>Ett fel uppstod vid upprättandet av RPC port %u för att lyssna på IPv4: %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> - <translation>Frigör block- och adressdatabaser vid nedstängning. Detta ökar tiden för nedstängning (standard: 0)</translation> + <translation>Frigör block- och adressdatabaser vid nedstängning. Detta ökar tiden för nedstängning (förvalt: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>Lyssna på JSON-RPC-anslutningar på <port> (förvalt: 8332 eller testnet: 18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Tillåt kommandon från kommandotolken och JSON-RPC-kommandon</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>Importerar blockkedjans datafil.</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>Importerar bootstrap blockkedjans datafil.</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Kör i bakgrunden som tjänst och acceptera kommandon</translation> </message> @@ -2195,14 +2210,14 @@ Adress: %4 <translation>Använd testnätverket</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> - <translation>Acceptera anslutningar utifrån (standard: 1 om ingen -proxy eller -connect)</translation> + <translation>Acceptera anslutningar utifrån (förvalt: 1 om ingen -proxy eller -connect)</translation> </message> <message> <location line="-20"/> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)</source> - <translation>Sätt den maximala storleken av hög-prioriterade/låg-avgifts transaktioner i byte (standard: 27000)</translation> + <translation>Sätt den maximala storleken av hög-prioriterade/låg-avgifts transaktioner i byte (förvalt: 27000)</translation> </message> <message> <location line="+5"/> @@ -2220,12 +2235,7 @@ Adress: %4 <translation>Varning: Vänligen kolla så att din dators datum och tid är korrekt! Om din klocka går fel kommer Bitcoin inte fungera korrekt.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>Ett fel uppstod vid upprättandet av RPC port %i för att lyssna: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Block skapande inställningar:</translation> </message> @@ -2237,7 +2247,7 @@ Adress: %4 <message> <location line="+3"/> <source>Discover own IP address (default: 1 when listening and no -externalip)</source> - <translation>Hitta egen IP-adress (standard: 1 under lyssning och utan -externalip)</translation> + <translation>Hitta egen IP-adress (förvalt: 1 under lyssning och utan -externalip)</translation> </message> <message> <location line="+11"/> @@ -2247,27 +2257,22 @@ Adress: %4 <message> <location line="+2"/> <source>Find peers using DNS lookup (default: 1 unless -connect)</source> - <translation>Söl efter klienter med DNS sökningen (standard: 1 om inte -connect)</translation> + <translation>Sök efter klienter med DNS sökningen (förvalt: 1 om inte -connect)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Importerar block...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Ogiltig -tor adress: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> - <translation>Maximal buffert för mottagning per anslutning, <n>*1000 byte (förval: 5000)</translation> + <translation>Maximal buffert för mottagning per anslutning, <n>*1000 byte (förvalt: 5000)</translation> </message> <message> <location line="+1"/> <source>Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)</source> - <translation>Maximal buffert för sändning per anslutning, <n>*1000 byte (förval: 5000)</translation> + <translation>Maximal buffert för sändning per anslutning, <n>*1000 byte (förvalt: 5000)</translation> </message> <message> <location line="+1"/> @@ -2297,7 +2302,7 @@ Adress: %4 <message> <location line="+1"/> <source>Select the version of socks proxy to use (4-5, default: 5)</source> - <translation>Välj socks-proxy version att använda (4 eller 5, standard: 5)</translation> + <translation>Välj socks-proxy version att använda (4-5, förvalt: 5)</translation> </message> <message> <location line="+3"/> @@ -2312,37 +2317,37 @@ Adress: %4 <message> <location line="+7"/> <source>Set maximum block size in bytes (default: 250000)</source> - <translation>Sätt maximal blockstorlek i byte (standard: 250000)</translation> + <translation>Sätt maximal blockstorlek i byte (förvalt: 250000)</translation> </message> <message> <location line="+1"/> <source>Set minimum block size in bytes (default: 0)</source> - <translation>Sätt minsta blockstorlek i byte (standard: 0)</translation> + <translation>Sätt minsta blockstorlek i byte (förvalt: 0)</translation> </message> <message> <location line="+1"/> <source>Shrink debug.log file on client startup (default: 1 when no -debug)</source> - <translation>Krymp debug.log filen vid klient start (standard: 1 vid ingen -debug)</translation> + <translation>Krymp debug.log filen vid klient start (förvalt: 1 vid ingen -debug)</translation> </message> <message> <location line="+2"/> <source>Specify connection timeout in milliseconds (default: 5000)</source> - <translation>Ange timeout för uppkoppling i millisekunder (standard: 5000)</translation> + <translation>Ange timeout för uppkoppling i millisekunder (förvalt: 5000)</translation> </message> <message> <location line="+13"/> <source>Use UPnP to map the listening port (default: 0)</source> - <translation>Använd UPnP för att mappa den lyssnande porten (standard: 0)</translation> + <translation>Använd UPnP för att mappa den lyssnande porten (förvalt: 0)</translation> </message> <message> <location line="+1"/> <source>Use UPnP to map the listening port (default: 1 when listening)</source> - <translation>Använd UPnP för att mappa den lyssnande porten (standard: 1 under lyssning)</translation> + <translation>Använd UPnP för att mappa den lyssnande porten (förvalt: 1 under lyssning)</translation> </message> <message> <location line="+1"/> <source>Use proxy to reach tor hidden services (default: same as -proxy)</source> - <translation>Använd en proxy för att nå tor (standard: samma som -proxy)</translation> + <translation>Använd en proxy för att nå tor (förvalt: samma som -proxy)</translation> </message> <message> <location line="+2"/> @@ -2365,34 +2370,29 @@ Adress: %4 <translation>Lösenord för JSON-RPC-anslutningar</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Lyssna på JSON-RPC-anslutningar på <port> (förval: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Tillåt JSON-RPC-anslutningar från specifika IP-adresser</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> - <translation>Skicka kommandon till klient på <ip> (förval: 127.0.0.1)</translation> + <translation>Skicka kommandon till klient på <ip> (förvalt: 127.0.0.1)</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Exekvera kommando när bästa blocket ändras (%s i cmd är utbytt av blockhash)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Uppgradera plånboken till senaste formatet</translation> </message> <message> <location line="-15"/> <source>Set key pool size to <n> (default: 100)</source> - <translation>Sätt storleken på nyckelpoolen till <n> (förval: 100)</translation> + <translation>Sätt storleken på nyckelpoolen till <n> (förvalt: 100)</translation> </message> <message> <location line="-14"/> @@ -2402,50 +2402,50 @@ Adress: %4 <message> <location line="-24"/> <source>How many blocks to check at startup (default: 2500, 0 = all)</source> - <translation>Hur många block att kontrollera vid uppstart (standardvärde: 2500, 0 = alla)</translation> + <translation>Hur många block att kontrollera vid uppstart (förvalt: 2500, 0 = alla)</translation> </message> <message> <location line="+1"/> <source>How thorough the block verification is (0-6, default: 1)</source> - <translation>Hur grundlig blockverifikationen är (0-6, standardvärde: 1)</translation> + <translation>Hur grundlig blockverifikationen är (0-6, förvalt: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Inporterar block från extern blk000?.dat fil</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Använd OpenSSL (https) för JSON-RPC-anslutningar</translation> </message> <message> <location line="-21"/> <source>Server certificate file (default: server.cert)</source> - <translation>Serverns certifikatfil (förval: server.cert)</translation> + <translation>Serverns certifikatfil (förvalt: server.cert)</translation> </message> <message> <location line="+1"/> <source>Server private key (default: server.pem)</source> - <translation>Serverns privata nyckel (förval: server.pem)</translation> + <translation>Serverns privata nyckel (förvalt: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> - <translation>Accepterade krypteringsalgoritmer (förval: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> + <translation>Accepterade krypteringsalgoritmer (förvalt: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Det här hjälp medelandet</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Kan inte låsa data-mappen %s. Bitcoin körs förmodligen redan.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2460,12 +2460,12 @@ Adress: %4 <translation>Anslut genom socks-proxy</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Tillåt DNS-sökningar för -addnode, -seednode och -connect</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Laddar adresser...</translation> </message> @@ -2495,12 +2495,12 @@ Adress: %4 <translation>Fel vid inläsning av plånboksfilen wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Ogiltig -proxy adress: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Okänt nätverk som anges i -onlynet: '%s'</translation> </message> @@ -2520,12 +2520,12 @@ Adress: %4 <translation>Kan inte matcha -externalip adress: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Ogiltigt belopp för -paytxfee=<belopp>:'%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Fel: kunde inte starta nod</translation> </message> @@ -2535,12 +2535,12 @@ Adress: %4 <translation>Fel: Plånboken är låst, det går ej att skapa en transaktion </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Fel: Denna transaktion kräver en transaktionsavgift på minst %s på grund av dess storlek, komplexitet, eller användning av senast mottagna bitcoins </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Fel: Transaktionen gick inte att skapa </translation> </message> @@ -2550,12 +2550,12 @@ Adress: %4 <translation>Skickar...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Fel: Transaktionen avslogs. Detta kan hända om några av mynten i plånboken redan spenderats, t.ex om du använt en kopia av wallet.dat och mynt spenderades i kopian men inte markerats som spenderas här.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Ogiltig mängd</translation> </message> @@ -2565,12 +2565,12 @@ Adress: %4 <translation>Otillräckligt med bitcoins</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Laddar blockindex...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Lägg till en nod att koppla upp mot och försök att hålla anslutningen öppen</translation> </message> @@ -2580,9 +2580,9 @@ Adress: %4 <translation>Det går inte att binda till %s på den här datorn. Bitcoin är förmodligen redan igång.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> - <translation>Sök efter klienter med internet relay chat (standard: 0)</translation> + <translation>Sök efter klienter med internet relay chat (förvalt: 0)</translation> </message> <message> <location line="-2"/> @@ -2625,7 +2625,7 @@ Adress: %4 <translation>Att använda %s alternativet</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2644,12 +2644,12 @@ Om filen inte existerar, skapa den med enbart ägarläsbara filrättigheter. </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Fel</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_tr.ts index de9d4ea9ad..56c8b8d3a4 100644 --- a/src/qt/locale/bitcoin_tr.ts +++ b/src/qt/locale/bitcoin_tr.ts @@ -82,11 +82,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>Mesaj &imzala</translation> + <source>Sign &Message</source> + <translation>&Mesaj imzala</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>Seçili adresi listeden sil</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>Belirtilen Bitcoin adresi ile imzalandığını doğrulamak için bir mesajı kontrol et</translation> </message> @@ -96,12 +101,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Mesaj &kontrol et</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Seçilen adresi listeden siler. Sadece gönderi adresleri silinebilir.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Sil</translation> </message> @@ -232,24 +232,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Cüzdanınızı şifrelemek istediğinizden emin misiniz?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>ÖNEMLİ: Önceden yapmış olduğunuz cüzdan dosyası yedeklemelerinin yeni oluşturulan şifrelenmiş cüzdan dosyası ile değiştirilmeleri gerekir. Güvenlik nedenleriyle yeni, şifrelenmiş cüzdanı kullanmaya başladığınızda eski şifrelenmemiş cüzdan dosyaları işe yaramaz hale gelecektir.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Uyarı: Caps Lock tuşu faal durumda!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Cüzdan şifrelendi</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Şifreleme işlemini tamamlamak için Bitcoin şimdi kapanacaktır. Cüzdanınızı şifrelemenin, Bitcoinlerinizin bilgisayara bulaşan kötücül bir yazılım tarafından çalınmaya karşı tamamen koruyamayacağını unutmayınız.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>&Mesaj imzala...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Şebeke ile senkronizasyon...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Genel bakış</translation> </message> @@ -313,7 +318,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Cüzdana genel bakışı göster</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&Muameleler</translation> </message> @@ -333,7 +338,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Saklanan adres ve etiket listesini düzenle</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>Bitcoin &al</translation> </message> @@ -343,12 +348,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Ödeme alma adreslerinin listesini göster</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>Bitcoin &yolla</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Çık</translation> </message> @@ -378,7 +383,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Seçenekler...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>Cüzdanı &şifrele...</translation> </message> @@ -403,42 +408,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Muamele tarihçesinden %1 blok indirildi (toplam %2 blok, %%3 tamamlandı).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Dışa aktar...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Bir Bitcoin adresine Bitcoin yolla</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Bir adresin sizin olduğunu ispatlamak için mesaj imzalayın</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Belirtilen Bitcoin adresi ile imzalandığını doğrulamak için bir mesajı kontrol et</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>İ&mzalar</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Bitcoin seçeneklerinin yapılandırmasını değiştir</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Güncel sekmedeki verileri bir dosyaya aktar</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Cüzdanı şifrele ya da şifreyi aç</translation> </message> @@ -453,7 +443,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Cüzdan şifrelemesi için kullanılan parolayı değiştir</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&Hata ayıklama penceresi</translation> </message> @@ -463,12 +453,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Hata ayıklama ve teşhis penceresini aç</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>Mesaj &kontrol et...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,7 +468,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Cüzdan</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>Bitcoin &Hakkında</translation> </message> @@ -488,12 +478,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Göster / Sakla</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Dosya</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Ayarlar</translation> </message> @@ -508,7 +498,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Sekme araç çubuğu</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Faaliyet araç çubuğu</translation> </message> @@ -525,7 +515,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Bitcoin istemcisi</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>Bitcoin şebekesine %n faal bağlantı</numerusform></translation> </message> @@ -603,7 +593,7 @@ Adres: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>URI yönetimi</translation> @@ -1110,7 +1100,7 @@ Adres: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>Mevcut değil</translation> </message> @@ -1261,8 +1251,8 @@ Adres: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>Alıcı &ekle</translation> + <source>Add &Recipient</source> + <translation>&Alıcı ekle</translation> </message> <message> <location line="+20"/> @@ -1291,8 +1281,8 @@ Adres: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Gönder</translation> + <source>S&end</source> + <translation>G&önder</translation> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1425,7 +1415,7 @@ Adres: %4 <location line="+13"/> <location line="+124"/> <source>&Sign Message</source> - <translation>Mesaj &İmzala</translation> + <translation>Mesaj &imzala</translation> </message> <message> <location line="-118"/> @@ -1647,7 +1637,7 @@ Adres: %4 <translation>Oluşturuldu</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Gönderen</translation> @@ -1758,12 +1748,12 @@ Adres: %4 <translation>yanlış</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, henüz başarılı bir şekilde yayınlanmadı</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>bilinmiyor</translation> </message> @@ -2074,7 +2064,7 @@ Adres: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Bitcoin sürümü</translation> </message> @@ -2089,12 +2079,12 @@ Adres: %4 <translation>-server ya da bitcoind'ye komut gönder</translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Komutları listele</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Bir komut için yardım al</translation> </message> @@ -2169,22 +2159,47 @@ Adres: %4 <translation>Aksaklık gösteren eşlerle bağlantıyı kesme sınırı (varsayılan: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Aksaklık gösteren eşlerle yeni bağlantıları engelleme süresi, saniye olarak (varsayılan: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>IPv6 üzerinde dinlemek için %i numaralı RPC portunun kurulumu sırasında hata meydana geldi, IPv4'e dönülüyor: %s </translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>IPv4 üzerinde dinlemek için %u numaralı RPC portunun kurulumu sırasında hata meydana geldi: %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>Blok ve adres veri tabanlarını ayır. Kapatma süresini arttırır (varsayılan: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>JSON-RPC bağlantılarını <port> üzerinde dinle (varsayılan: 8332 veya tesnet: 18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Konut satırı ve JSON-RPC komutlarını kabul et</translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>Blok zinciri veri dosyası içe aktarılıyor.</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>Blok zinciri verileri başlatma dosyası içe aktarılıyor.</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Arka planda daemon (servis) olarak çalış ve komutları kabul et</translation> </message> @@ -2194,7 +2209,7 @@ Adres: %4 <translation>Deneme şebekesini kullan</translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Dışarıdan gelen bağlantıları kabul et (varsayılan: -proxy veya -connect yoksa 1)</translation> </message> @@ -2219,12 +2234,7 @@ Adres: %4 <translation>Uyarı: Lütfen bilgisayarınızın tarih ve saatinin doğru olup olmadığını kontrol ediniz! Saatiniz doğru değilse Bitcoin gerektiği gibi çalışamaz.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>%i RPC portunun dinleme için kurulması sırasında bir hata meydana geldi: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>Blok oluşturma seçenekleri:</translation> </message> @@ -2249,17 +2259,12 @@ Adres: %4 <translation>Eşleri DNS araması vasıtasıyla bul (varsayılan: 1, eğer -connect kullanılmadıysa)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>Bloklar içe aktarılıyor...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>Geçersiz -tor adresi: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Bağlantı başına azami alım tamponu, <n>*1000 bayt (varsayılan: 5000)</translation> </message> @@ -2364,27 +2369,22 @@ Adres: %4 <translation>JSON-RPC bağlantıları için parola</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>JSON-RPC bağlantıları için dinlenecek <port> (varsayılan: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Belirtilen İP adresinden JSON-RPC bağlantılarını kabul et</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Şu <ip> adresinde (varsayılan: 127.0.0.1) çalışan düğüme komut yolla</translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>En iyi blok değiştiğinde komutu çalıştır (komut için %s parametresi blok hash değeri ile değiştirilecektir)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>Cüzdanı en yeni biçime güncelle</translation> </message> @@ -2409,12 +2409,12 @@ Adres: %4 <translation>Blok kontrolünün derinliği (0 ilâ 6, varsayılan: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>Harici blk000?.dat dosyasından blokları içe aktarır</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>JSON-RPC bağlantıları için OpenSSL (https) kullan</translation> </message> @@ -2429,22 +2429,22 @@ Adres: %4 <translation>Sunucu özel anahtarı (varsayılan: server.pem)</translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Kabul edilebilir şifreler (varsayılan: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Bu yardım mesajı</translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>%s veri dizininde kilit elde edilemedi. Bitcoin muhtemelen hâlihazırda çalışmaktadır.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2459,12 +2459,12 @@ Adres: %4 <translation>Socks vekil sunucusu vasıtasıyla bağlan</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>-addnode, -seednode ve -connect için DNS aramalarına izin ver</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Adresler yükleniyor...</translation> </message> @@ -2494,12 +2494,12 @@ Adres: %4 <translation>wallet.dat dosyasının yüklenmesinde hata oluştu</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Geçersiz -proxy adresi: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>-onlynet için bilinmeyen bir şebeke belirtildi: '%s'</translation> </message> @@ -2519,12 +2519,12 @@ Adres: %4 <translation>-externalip adresi çözümlenemedi: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>-paytxfee=<miktar> için geçersiz miktar: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>Hata: düğüm başlatılamadı</translation> </message> @@ -2534,12 +2534,12 @@ Adres: %4 <translation>Hata: Cüzdan kilitli, muamele oluşturulamadı </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Hata: Muamelenin miktarı, karmaşıklığı ya da yakın geçmişte alınan fonların kullanılması nedeniyle bu muamele en az %s tutarında ücret gerektirmektedir </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Hata: Muamele oluşturması başarısız oldu </translation> </message> @@ -2549,12 +2549,12 @@ Adres: %4 <translation>Gönderiliyor...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Hata: Muamele reddedildi. Cüzdanınızdaki madenî paraların bazıları zaten harcanmış olduğunda bu meydana gelebilir. Örneğin wallet.dat dosyasının bir kopyasını kullandıysanız ve kopyada para harcandığında ancak burada harcandığı işaretlenmediğinde.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>Geçersiz miktar</translation> </message> @@ -2564,12 +2564,12 @@ Adres: %4 <translation>Yetersiz bakiye</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Blok indeksi yükleniyor...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Bağlanılacak düğüm ekle ve bağlantıyı zinde tutmaya çalış</translation> </message> @@ -2579,7 +2579,7 @@ Adres: %4 <translation>Bu bilgisayarda %s unsuruna bağlanılamadı. Bitcoin muhtemelen hâlihazırda çalışmaktadır.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Eşleri Internet Relay Chat vasıtasıyla bul (varsayılan: 0)</translation> </message> @@ -2624,7 +2624,7 @@ Adres: %4 <translation>%s seçeneğini kullanmak için</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2643,12 +2643,12 @@ Dosya mevcut değilse, sadece sahibi için okumayla sınırlı izin ile oluştur </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>Hata</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts index 4c03feb605..beef3f9d3f 100644 --- a/src/qt/locale/bitcoin_uk.ts +++ b/src/qt/locale/bitcoin_uk.ts @@ -82,11 +82,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&Підписати повідомлення</translation> + <source>Sign &Message</source> + <translation type="unfinished"/> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation type="unfinished"/> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation type="unfinished"/> </message> @@ -96,12 +101,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>Видалити виділену адресу зі списку. Лише адреси з адресної книги можуть бути видалені.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&Видалити</translation> </message> @@ -232,24 +232,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Ви дійсно хочете зашифрувати свій гаманець?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>Увага: Ввімкнено Caps Lock</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>Гаманець зашифровано</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>Біткоін-клієнт буде закрито для завершення процесу шифрування. Пам’ятайте, що шифрування гаманця не може повністю захистити ваші біткоіни від кражі, у випадку якщо ваш комп’ютер буде інфіковано шкідливими програмами.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>&Підписати повідомлення...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>Синхронізація з мережею...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&Огляд</translation> </message> @@ -313,7 +318,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Показати загальний огляд гаманця</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>Пе&реклади</translation> </message> @@ -333,7 +338,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Редагувати список збережених адрес та міток</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>О&тримати</translation> </message> @@ -343,12 +348,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Показати список адрес для отримання платежів</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>В&ідправити</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>&Вихід</translation> </message> @@ -378,7 +383,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Параметри...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&Шифрування гаманця...</translation> </message> @@ -403,42 +408,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Завантажено %1 з %2 блоків історії переказів (%3% done).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&Експорт...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>Відправити монети на вказану адресу</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Підпишіть повідомлення щоб довести, що ви є власником цієї адреси</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>Редагувати параметри</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>Експортувати дані з поточної вкладки в файл</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>Зашифрувати чи розшифрувати гаманець</translation> </message> @@ -453,7 +443,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Змінити пароль, який використовується для шифрування гаманця</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation type="unfinished"/> </message> @@ -463,12 +453,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation type="unfinished"/> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -478,7 +468,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Гаманець</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&Про Bitcoin</translation> </message> @@ -488,12 +478,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&Файл</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&Налаштування</translation> </message> @@ -508,7 +498,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Панель вкладок</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>Панель дій</translation> </message> @@ -525,7 +515,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation type="unfinished"/> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n активне з’єднання з мережею</numerusform><numerusform>%n активні з’єднання з мережею</numerusform><numerusform>%n активних з’єднань з мережею</numerusform></translation> </message> @@ -603,7 +593,7 @@ Address: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation type="unfinished"/> @@ -1111,7 +1101,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation type="unfinished"/> </message> @@ -1262,8 +1252,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>Дод&ати одержувача</translation> + <source>Add &Recipient</source> + <translation type="unfinished"/> </message> <message> <location line="+20"/> @@ -1292,8 +1282,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&Відправити</translation> + <source>S&end</source> + <translation type="unfinished"/> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1648,7 +1638,7 @@ Address: %4 <translation>Згенеровано</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>Відправник</translation> @@ -1759,12 +1749,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, ще не було успішно розіслано</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>невідомий</translation> </message> @@ -2075,7 +2065,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>Версія</translation> </message> @@ -2091,13 +2081,13 @@ Address: %4 </translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>Список команд </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>Отримати довідку по команді </translation> @@ -2179,23 +2169,48 @@ Address: %4 <translation>Поріг відключення неправильно підєднаних пірів (за замовчуванням: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Максимальній розмір вхідного буферу на одне з'єднання (за замовчуванням 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation type="unfinished"/> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>Приймати команди із командного рядка та команди JSON-RPC </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation type="unfinished"/> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>Запустити в фоновому режимі (як демон) та приймати команди </translation> @@ -2207,7 +2222,7 @@ Address: %4 </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation type="unfinished"/> </message> @@ -2232,12 +2247,7 @@ Address: %4 <translation>Увага: будь ласка, перевірте дату і час на свому комп’ютері. Якщо ваш годинник йде неправильно, Bitcoin може працювати некоректно.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation type="unfinished"/> </message> @@ -2262,17 +2272,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation type="unfinished"/> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation type="unfinished"/> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>Максимальоий буфер , <n> * 1000 байт (за умовчанням: 5000)</translation> </message> @@ -2379,30 +2384,24 @@ Address: %4 </translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>Прослуховувати <port> для JSON-RPC-з’єднань (за промовчуванням: 8332) -</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Дозволити JSON-RPC-з’єднання з вказаної IP-адреси </translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Відправляти команди на вузол, запущений на <ip> (за промовчуванням: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation type="unfinished"/> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation type="unfinished"/> </message> @@ -2429,12 +2428,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation type="unfinished"/> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Використовувати OpenSSL (https) для JSON-RPC-з’єднань </translation> @@ -2452,24 +2451,24 @@ Address: %4 </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Допустимі шифри (за промовчуванням: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) </translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>Дана довідка </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>Неможливо встановити блокування на робочий каталог %s. Можливо, гаманець вже запущено.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> @@ -2484,13 +2483,13 @@ Address: %4 <translation>Підключитись через SOCKS-проксі</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Дозволити пошук в DNS для команд «addnode» і «connect» </translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>Завантаження адрес...</translation> </message> @@ -2520,12 +2519,12 @@ Address: %4 <translation>Помилка при завантаженні wallet.dat</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>Помилка в адресі проксі-сервера: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation type="unfinished"/> </message> @@ -2545,12 +2544,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Помилка у величині комісії: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation type="unfinished"/> </message> @@ -2560,12 +2559,12 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>Цей переказ перевищує максимально допустимий розмір. Проте ви можете здійснити її, додавши комісію в %1, яка відправиться тим вузлам що оброблять ваш переказ, та допоможе підтримати мережу. Ви хочете додати комісію?</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>Помилка: не вдалося створити переказ </translation> </message> @@ -2575,12 +2574,12 @@ Address: %4 <translation>Відправлення...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Помилка: переказ було відхилено. Це може статись, якщо декілька монет з вашого гаманця вже використані, наприклад, якщо ви використовуєте одну копію гаманця (wallet.dat), а монети були використані з іншої копії, але не позначені як використані в цій.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation type="unfinished"/> </message> @@ -2590,12 +2589,12 @@ Address: %4 <translation>Недостатньо коштів</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>Завантаження індексу блоків...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Додати вузол для підключення and attempt to keep the connection open</translation> </message> @@ -2605,7 +2604,7 @@ Address: %4 <translation>Неможливо прив’язати до порту %s на цьому комп’ютері. Молживо гаманець вже запущено.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation type="unfinished"/> </message> @@ -2651,7 +2650,7 @@ Address: %4 <translation type="unfinished"/> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2663,12 +2662,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation type="unfinished"/> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation type="unfinished"/> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts index bfe13fcf1c..32db36a83b 100644 --- a/src/qt/locale/bitcoin_zh_CN.ts +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -82,11 +82,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> - <translation>&签名消息</translation> + <source>Sign &Message</source> + <translation>对消息签名</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>从列表中删除选中的地址</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>验证消息,确保消息是由指定的比特币地址签名过的。</translation> </message> @@ -96,12 +101,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&验证消息</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>从列表中删除当前选中地址。只有发送地址可以被删除。</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>&删除</translation> </message> @@ -232,24 +232,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>您确定需要为钱包加密吗?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>重要提示:您以前备份的钱包文件应该替换成最新生成的加密钱包文件(重新备份)。从安全性上考虑,您以前备份的未加密的钱包文件,在您使用新的加密钱包后将无效,请重新备份。</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>警告:大写锁定键处于打开状态!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>钱包已加密</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>将关闭软件以完成加密过程。 请您谨记:钱包加密并不是万能的,电脑中毒,您的比特币还是有可能丢失。</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>对&消息签名...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>正在与网络同步...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>&概况</translation> </message> @@ -313,7 +318,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>显示钱包概况</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>&交易记录</translation> </message> @@ -333,7 +338,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>修改存储的地址和标签列表</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>&收款地址</translation> </message> @@ -343,12 +348,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>显示接收支付的地址列表</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>&发送货币</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>退出</translation> </message> @@ -378,7 +383,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&选项...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>&加密钱包...</translation> </message> @@ -403,42 +408,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>已下载 %2 个交易历史数据块中的 %1 个 (完成率 %3% ).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>&导出...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>向一个比特币地址发送比特币</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>签名消息,证明这个地址属于您。</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>验证消息,确保消息是由指定的比特币地址签名过的。</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>&签名</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>设置选项</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>导出当前数据到文件</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>加密或解密钱包</translation> </message> @@ -453,7 +443,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>修改钱包加密口令</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>&调试窗口</translation> </message> @@ -463,12 +453,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>在诊断控制台调试</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>&验证消息...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>比特币</translation> </message> @@ -478,7 +468,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>钱包</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>&关于比特币</translation> </message> @@ -488,12 +478,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&显示 / 隐藏</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>&文件</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>&设置</translation> </message> @@ -508,7 +498,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>分页工具栏</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>动作工具栏</translation> </message> @@ -525,7 +515,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>比特币客户端</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>到比特币网络的连接共有%n条</numerusform></translation> </message> @@ -603,7 +593,7 @@ Address: %4 </translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>URI 处理</translation> @@ -1111,7 +1101,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>不可用</translation> </message> @@ -1262,8 +1252,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> - <translation>&添加接收人</translation> + <source>Add &Recipient</source> + <translation>添加收款人</translation> </message> <message> <location line="+20"/> @@ -1292,8 +1282,8 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> - <translation>&发送</translation> + <source>S&end</source> + <translation>发送</translation> </message> <message> <location filename="../sendcoinsdialog.cpp" line="-59"/> @@ -1648,7 +1638,7 @@ Address: %4 <translation>生成</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>来自</translation> @@ -1759,12 +1749,12 @@ Address: %4 <translation>错误</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, 未被成功广播</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>未知</translation> </message> @@ -2075,7 +2065,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>比特币版本</translation> </message> @@ -2091,13 +2081,13 @@ Address: %4 </translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>列出命令 </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>获得某条命令的帮助 </translation> @@ -2179,23 +2169,48 @@ Address: %4 <translation>Threshold for disconnecting misbehaving peers (缺省: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Number of seconds to keep misbehaving peers from reconnecting (缺省: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>IPv6下设置RPC端口 %i 时发生错误,返回到IPv4:%s</translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>设置RPC监听端口%u时发生错误, IPv4:%s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>分离数据块数据库和地址数据库. 会延长关闭时间 (缺省: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>JSON-RPC连接监听端口<port> (缺省:8332 testnet:18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>接受命令行和 JSON-RPC 命令 </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>正在导入数据链文件</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>正在导入引导数据链文件</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>在后台运行并接受命令 @@ -2208,7 +2223,7 @@ Address: %4 </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>接受来自外部的连接 (缺省: 如果不带 -proxy or -connect 参数设置为1)</translation> </message> @@ -2233,12 +2248,7 @@ Address: %4 <translation>警告:请检查电脑的日期时间设置是否正确!时间错误可能会导致比特币客户端运行异常。</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>设置RPC监听端口%i时发生错误: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>数据块创建选项:</translation> </message> @@ -2263,17 +2273,12 @@ Address: %4 <translation>通过DNS查找节点(缺省:1 除非使用 -connect 选项)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>数据块导入中...</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>非法的 -tor 地址:'%s' </translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>每个连接的最大接收缓存,<n>*1000 字节(缺省:5000)</translation> </message> @@ -2380,30 +2385,24 @@ Address: %4 </translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>JSON-RPC连接监听<端口> (默认为 8332) -</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>允许从指定IP接受到的JSON-RPC连接 </translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>向IP地址为 <ip> 的节点发送指令 (缺省: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>当最佳数据块变化时执行命令 (命令行中的 %s 会被替换成数据块哈希值)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>将钱包升级到最新的格式</translation> </message> @@ -2430,12 +2429,12 @@ Address: %4 <translation>需要几个确认 (0-6个, 缺省: 1个)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>从外来文件 blk000?.dat 导入数据块</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>为 JSON-RPC 连接使用 OpenSSL (https)连接</translation> </message> @@ -2452,24 +2451,24 @@ Address: %4 </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>可接受的加密器 (默认为 TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) </translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>该帮助信息 </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>无法给数据目录 %s 加锁。比特币进程可能已在运行。</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>比特币</translation> </message> @@ -2484,12 +2483,12 @@ Address: %4 <translation>通过 socks 代理连接</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>使用 -addnode, -seednode 和 -connect选项时允许DNS查找</translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>正在加载地址...</translation> </message> @@ -2519,12 +2518,12 @@ Address: %4 <translation>wallet.dat钱包文件加载错误</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>非法的代理地址: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>被指定的是未知网络 -onlynet: '%s'</translation> </message> @@ -2544,12 +2543,12 @@ Address: %4 <translation>无法解析 -externalip 地址: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>非法金额 -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>错误: 无法启动节点</translation> </message> @@ -2559,12 +2558,12 @@ Address: %4 <translation>错误: 钱包被锁,无法创建新的交易</translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>错误: 该交易需支付至少 %s 的交易费,原因可能是该交易数量太小、构成太复杂或者使用了新近接收到的比特币</translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>错误:交易创建失败。</translation> </message> @@ -2574,12 +2573,12 @@ Address: %4 <translation>发送中</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>错误:交易被拒绝。这种情况通常发生在您钱包中的一些货币已经被消费之后,比如您使用了一个wallet.dat的副本,而货币在那个副本中已经被消费,但在当前钱包中未被标记为已消费。</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>金额不对</translation> </message> @@ -2589,12 +2588,12 @@ Address: %4 <translation>金额不足</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>加载数据块索引...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>添加节点并与其保持连接</translation> </message> @@ -2604,7 +2603,7 @@ Address: %4 <translation>无法在本机绑定 %s 端口 . 比特币客户端软件可能已经在运行.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>通过IRC聊天室查找网络上的比特币节点 (缺省: 0)</translation> </message> @@ -2649,7 +2648,7 @@ Address: %4 <translation>使用 %s 选项</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2667,12 +2666,12 @@ rpcpassword=%s 如果配置文件不存在,请新建,并将文件权限设置为仅允许文件所有者读取.</translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>错误</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts index 6c9f67c7ec..f641d20137 100644 --- a/src/qt/locale/bitcoin_zh_TW.ts +++ b/src/qt/locale/bitcoin_zh_TW.ts @@ -82,11 +82,16 @@ This product includes software developed by the OpenSSL Project for use in the O </message> <message> <location line="+3"/> - <source>&Sign Message</source> + <source>Sign &Message</source> <translation>簽署訊息</translation> </message> <message> - <location line="+11"/> + <location line="+25"/> + <source>Delete the currently selected address from the list</source> + <translation>從列表中刪除目前選取的位址</translation> + </message> + <message> + <location line="-14"/> <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> <translation>驗證一則訊息來確認它是用指定的位元幣位址簽署的</translation> </message> @@ -96,12 +101,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>驗證訊息</translation> </message> <message> - <location line="+11"/> - <source>Delete the currently selected address from the list. Only sending addresses can be deleted.</source> - <translation>從列表中刪除目前選取的位址. 只能夠刪除付款位址.</translation> - </message> - <message> - <location line="+3"/> + <location line="+14"/> <source>&Delete</source> <translation>刪除</translation> </message> @@ -232,24 +232,29 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>你確定要將錢包加密嗎?</translation> </message> <message> - <location line="+106"/> + <location line="+15"/> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>重要: 請改用新產生有加密的錢包檔, 來取代之前錢包檔的備份. 為了安全性的理由, 當你開始使用新的有加密的錢包時, 舊錢包的備份就不能再使用了.</translation> + </message> + <message> + <location line="+100"/> <location line="+24"/> <source>Warning: The Caps Lock key is on!</source> <translation>警告: 大寫字母鎖定作用中!</translation> </message> <message> - <location line="-121"/> - <location line="+49"/> + <location line="-130"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation>錢包已加密</translation> </message> <message> - <location line="-48"/> + <location line="-56"/> <source>Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation>位元幣現在要關閉以完成加密程序. 請記住, 加密錢包無法完全防止入侵電腦的惡意程式偷取你的位元幣.</translation> </message> <message> - <location line="+5"/> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -293,17 +298,17 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+228"/> + <location filename="../bitcoingui.cpp" line="+257"/> <source>Sign &message...</source> <translation>訊息簽署...</translation> </message> <message> - <location line="+295"/> + <location line="+237"/> <source>Synchronizing with network...</source> <translation>網路同步中...</translation> </message> <message> - <location line="-325"/> + <location line="-299"/> <source>&Overview</source> <translation>總覽</translation> </message> @@ -313,7 +318,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>顯示錢包一般總覽</translation> </message> <message> - <location line="+5"/> + <location line="+17"/> <source>&Transactions</source> <translation>交易</translation> </message> @@ -333,7 +338,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>編輯位址與標記的儲存列表</translation> </message> <message> - <location line="+5"/> + <location line="-13"/> <source>&Receive coins</source> <translation>收錢</translation> </message> @@ -343,12 +348,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>顯示收款位址的列表</translation> </message> <message> - <location line="+5"/> + <location line="-7"/> <source>&Send coins</source> <translation>付錢</translation> </message> <message> - <location line="+41"/> + <location line="+35"/> <source>E&xit</source> <translation>結束</translation> </message> @@ -378,7 +383,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>選項...</translation> </message> <message> - <location line="+6"/> + <location line="+4"/> <source>&Encrypt Wallet...</source> <translation>錢包加密...</translation> </message> @@ -403,42 +408,27 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>已下載了全部 %2 個中的 %1 個交易紀錄區塊 (已完成 %3%).</translation> </message> <message> - <location line="-254"/> + <location line="-242"/> <source>&Export...</source> <translation>匯出...</translation> </message> <message> - <location line="-54"/> + <location line="-58"/> <source>Send coins to a Bitcoin address</source> <translation>付錢到一個位元幣位址</translation> </message> <message> - <location line="+6"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>簽署一則訊息來證明一個位元幣位址是你的</translation> - </message> - <message> - <location line="+4"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>驗證一則訊息來確認它是用指定的位元幣位址簽署的</translation> - </message> - <message> - <location line="+4"/> - <source>S&ignatures</source> - <translation>簽章</translation> - </message> - <message> - <location line="+37"/> + <location line="+45"/> <source>Modify configuration options for Bitcoin</source> <translation>修改位元幣的設定選項</translation> </message> <message> - <location line="+4"/> + <location line="+14"/> <source>Export the data in the current tab to a file</source> <translation>將目前分頁的資料匯出存成檔案</translation> </message> <message> - <location line="+2"/> + <location line="-10"/> <source>Encrypt or decrypt wallet</source> <translation>將錢包加解密</translation> </message> @@ -453,7 +443,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>變更錢包加密用的密碼</translation> </message> <message> - <location line="+1"/> + <location line="+6"/> <source>&Debug window</source> <translation>除錯視窗</translation> </message> @@ -463,12 +453,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>開啓除錯與診斷主控台</translation> </message> <message> - <location line="-55"/> + <location line="-5"/> <source>&Verify message...</source> <translation>訊息驗證...</translation> </message> <message> - <location line="-160"/> + <location line="-186"/> <source>Bitcoin</source> <translation>位元幣</translation> </message> @@ -478,7 +468,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>錢包</translation> </message> <message> - <location line="+195"/> + <location line="+168"/> <source>&About Bitcoin</source> <translation>關於位元幣</translation> </message> @@ -488,12 +478,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>顯示或隱藏</translation> </message> <message> - <location line="+34"/> + <location line="+39"/> <source>&File</source> <translation>檔案</translation> </message> <message> - <location line="+10"/> + <location line="+8"/> <source>&Settings</source> <translation>設定</translation> </message> @@ -508,7 +498,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>分頁工具列</translation> </message> <message> - <location line="+11"/> + <location line="+8"/> <source>Actions toolbar</source> <translation>動作工具列</translation> </message> @@ -525,7 +515,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>位元幣客戶端軟體</translation> </message> <message numerus="yes"> - <location line="+71"/> + <location line="+69"/> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>與位元幣網路有 %n 個連線在使用中</numerusform></translation> </message> @@ -602,7 +592,7 @@ Address: %4 位址: %4</translation> </message> <message> - <location line="+120"/> + <location line="+100"/> <location line="+15"/> <source>URI handling</source> <translation>URI 處理</translation> @@ -1110,7 +1100,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+328"/> + <location filename="../rpcconsole.cpp" line="+348"/> <source>N/A</source> <translation>無</translation> </message> @@ -1261,7 +1251,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Add Recipient</source> + <source>Add &Recipient</source> <translation>加收款人</translation> </message> <message> @@ -1291,7 +1281,7 @@ Address: %4 </message> <message> <location line="+3"/> - <source>&Send</source> + <source>S&end</source> <translation>付出</translation> </message> <message> @@ -1647,7 +1637,7 @@ Address: %4 <translation>生產出</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <location line="+17"/> <source>From</source> <translation>來處</translation> @@ -1758,12 +1748,12 @@ Address: %4 <translation>否</translation> </message> <message> - <location line="-212"/> + <location line="-211"/> <source>, has not been successfully broadcast yet</source> <translation>, 尚未成功公告出去</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>unknown</source> <translation>未知</translation> </message> @@ -2074,7 +2064,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+65"/> + <location filename="../bitcoinstrings.cpp" line="+71"/> <source>Bitcoin version</source> <translation>位元幣版本</translation> </message> @@ -2090,13 +2080,13 @@ Address: %4 </translation> </message> <message> - <location line="-20"/> + <location line="-19"/> <source>List commands</source> <translation>列出指令 </translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Get help for a command</source> <translation>取得指令說明 </translation> @@ -2178,23 +2168,48 @@ Address: %4 <translation>與亂搞的節點斷線的臨界值 (預設: 100)</translation> </message> <message> - <location line="-105"/> + <location line="-104"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>避免與亂搞的節點連線的秒數 (預設: 86400)</translation> </message> <message> - <location line="-12"/> + <location line="-22"/> + <source>An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s</source> + <translation>在 IPv6 網路上以通訊埠 %i 聽取 RPC 連線時發生錯誤, 退回使用 IPv4 網路: %s</translation> + </message> + <message> + <location line="+3"/> + <source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source> + <translation>在 IPv4 網路上以通訊埠 %u 聽取 RPC 連線時發生錯誤: %s</translation> + </message> + <message> + <location line="+5"/> <source>Detach block and address databases. Increases shutdown time (default: 0)</source> <translation>卸載區塊與位址的資料庫. 會延長關閉時間 (預設: 0)</translation> </message> <message> - <location line="+34"/> + <location line="+12"/> + <source>Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)</source> + <translation>在通訊埠 <port> 聽候 JSON-RPC 連線 (預設: 8332, 或若為測試網路: 18332)</translation> + </message> + <message> + <location line="+24"/> <source>Accept command line and JSON-RPC commands</source> <translation>接受命令列與 JSON-RPC 指令 </translation> </message> <message> - <location line="+61"/> + <location line="+36"/> + <source>Importing blockchain data file.</source> + <translation>匯入區塊鎖鏈資料檔.</translation> + </message> + <message> + <location line="+1"/> + <source>Importing bootstrap blockchain data file.</source> + <translation>匯入前導的區塊鎖鏈資料檔.</translation> + </message> + <message> + <location line="+23"/> <source>Run in the background as a daemon and accept commands</source> <translation>以背景程式執行並接受指令</translation> </message> @@ -2205,7 +2220,7 @@ Address: %4 </translation> </message> <message> - <location line="-93"/> + <location line="-92"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>是否接受外來連線 (預設: 當沒有 -proxy 或 -connect 時預設為 1)</translation> </message> @@ -2230,12 +2245,7 @@ Address: %4 <translation>警告: 請檢查電腦時間與日期是否正確! 位元幣無法在時鐘不準的情況下正常運作.</translation> </message> <message> - <location line="+13"/> - <source>An error occurred while setting up the RPC port %i for listening: %s</source> - <translation>設定聽候 RPC 連線的通訊埠 %i 時發生錯誤: %s</translation> - </message> - <message> - <location line="+4"/> + <location line="+16"/> <source>Block creation options:</source> <translation>區塊產生選項:</translation> </message> @@ -2260,17 +2270,12 @@ Address: %4 <translation>是否允許在找節點時使用域名查詢 (預設: 當沒用 -connect 時為 1)</translation> </message> <message> - <location line="+6"/> - <source>Importing blocks...</source> - <translation>匯入區塊</translation> - </message> - <message> - <location line="+4"/> + <location line="+11"/> <source>Invalid -tor address: '%s'</source> <translation>無效的 -tor 位址: '%s'</translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)</source> <translation>每個連線的接收緩衝區大小上限為 <n>*1000 個位元組 (預設: 5000)</translation> </message> @@ -2375,28 +2380,23 @@ Address: %4 <translation>JSON-RPC 連線密碼</translation> </message> <message> - <location line="-12"/> - <source>Listen for JSON-RPC connections on <port> (default: 8332)</source> - <translation>在通訊埠 <port> 聽候 JSON-RPC 連線 (預設: 8332)</translation> - </message> - <message> - <location line="-41"/> + <location line="-52"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>只允許從指定網路位址來的 JSON-RPC 連線</translation> </message> <message> - <location line="+61"/> + <location line="+60"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>送指令給在 <ip> 的節點 (預設: 127.0.0.1) </translation> </message> <message> - <location line="-90"/> + <location line="-91"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>當最新區塊改變時所要執行的指令 (指令中的 %s 會被取代為區塊的雜湊值)</translation> </message> <message> - <location line="+113"/> + <location line="+114"/> <source>Upgrade wallet to latest format</source> <translation>將錢包升級成最新的格式</translation> </message> @@ -2422,12 +2422,12 @@ Address: %4 <translation>區塊檢查的仔細程度 (0 至 6, 預設: 1)</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Imports blocks from external blk000?.dat file</source> <translation>從外來的區塊檔 blk000?.dat 匯入區塊</translation> </message> <message> - <location line="+52"/> + <location line="+51"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>於 JSON-RPC 連線使用 OpenSSL (https) </translation> @@ -2445,24 +2445,24 @@ Address: %4 </translation> </message> <message> - <location line="-110"/> + <location line="-116"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>可以接受的加密法 (預設: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) </translation> </message> <message> - <location line="+122"/> + <location line="+128"/> <source>This help message</source> <translation>此協助訊息 </translation> </message> <message> - <location line="-119"/> + <location line="-120"/> <source>Cannot obtain a lock on data directory %s. Bitcoin is probably already running.</source> <translation>無法鎖定資料目錄 %s. 也許位元幣已經在執行了.</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Bitcoin</source> <translation>位元幣</translation> </message> @@ -2477,12 +2477,12 @@ Address: %4 <translation>透過 SOCKS 代理伺服器連線</translation> </message> <message> - <location line="-13"/> + <location line="-12"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>允許對 -addnode, -seednode, -connect 的參數使用域名查詢 </translation> </message> <message> - <location line="+44"/> + <location line="+43"/> <source>Loading addresses...</source> <translation>載入位址中...</translation> </message> @@ -2512,12 +2512,12 @@ Address: %4 <translation>載入檔案 wallet.dat 失敗</translation> </message> <message> - <location line="+18"/> + <location line="+19"/> <source>Invalid -proxy address: '%s'</source> <translation>無效的 -proxy 位址: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>在 -onlynet 指定了不明的網路別: '%s'</translation> </message> @@ -2537,12 +2537,12 @@ Address: %4 <translation>無法解析 -externalip 位址: '%s'</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>設定 -paytxfee=<金額> 的金額無效: '%s'</translation> </message> <message> - <location line="-14"/> + <location line="-15"/> <source>Error: could not start node</source> <translation>錯誤: 無法啓動節點</translation> </message> @@ -2552,12 +2552,12 @@ Address: %4 <translation>錯誤: 錢包被上鎖了, 無法產生新的交易 </translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds </source> <translation>錯誤: 這筆交易需要至少 %s 的手續費, 因為它的金額太大, 或複雜度太高, 或是使用了最近才剛收到的款項 </translation> </message> <message> - <location line="+54"/> + <location line="+55"/> <source>Error: Transaction creation failed </source> <translation>錯誤: 交易產生失敗</translation> </message> @@ -2567,12 +2567,12 @@ Address: %4 <translation>付出中...</translation> </message> <message> - <location line="-100"/> + <location line="-101"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>錯誤: 交易被拒絕. 有時候會發生這種錯誤, 是因為你錢包中的一些錢已經被花掉了. 比如說你複製了錢包檔 wallet.dat, 然後用複製的錢包花掉了錢, 你現在所用的原來的錢包中卻沒有該筆交易紀錄.</translation> </message> <message> - <location line="+75"/> + <location line="+77"/> <source>Invalid amount</source> <translation>無效的金額</translation> </message> @@ -2582,12 +2582,12 @@ Address: %4 <translation>累積金額不足</translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Loading block index...</source> <translation>載入區塊索引中...</translation> </message> <message> - <location line="-46"/> + <location line="-45"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>加入一個要連線的節線, 並試著保持對它的連線暢通</translation> </message> @@ -2597,7 +2597,7 @@ Address: %4 <translation>無法和這台電腦上的 %s 繫結. 也許位元幣已經在執行了.</translation> </message> <message> - <location line="+48"/> + <location line="+47"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>是否使用網際網路中繼聊天(IRC)來找節點 (預設: 0)</translation> </message> @@ -2642,7 +2642,7 @@ Address: %4 <translation>為了要使用 %s 選項</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>%s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: @@ -2661,12 +2661,12 @@ rpcpassword=%s </translation> </message> <message> - <location line="+74"/> + <location line="+80"/> <source>Error</source> <translation>錯誤</translation> </message> <message> - <location line="-30"/> + <location line="-29"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 8025126c25..3fb30a9518 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -139,7 +139,7 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->socksVersion, OptionsModel::ProxySocksVersion); /* Window */ -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray); mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose); #endif diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index caa33414b2..1b2f18eab3 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -145,18 +145,18 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const case ProxyUse: return settings.value("fUseProxy", false); case ProxyIP: { - CService addrProxy; - if (GetProxy(NET_IPV4, addrProxy)) - return QVariant(QString::fromStdString(addrProxy.ToStringIP())); + proxyType proxy; + if (GetProxy(NET_IPV4, proxy)) + return QVariant(QString::fromStdString(proxy.first.ToStringIP())); else return QVariant(QString::fromStdString("127.0.0.1")); } case ProxyPort: { - CService addrProxy; - if (GetProxy(NET_IPV4, addrProxy)) - return QVariant(addrProxy.GetPort()); + proxyType proxy; + if (GetProxy(NET_IPV4, proxy)) + return QVariant(proxy.first.GetPort()); else - return 9050; + return QVariant(9050); } case ProxySocksVersion: return settings.value("nSocksVersion", 5); @@ -176,6 +176,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const } return QVariant(); } + bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role) { bool successful = true; /* set to false on parse error */ @@ -204,29 +205,37 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in settings.setValue("fUseProxy", value.toBool()); ApplyProxySettings(); break; - case ProxyIP: - { - CService addrProxy("127.0.0.1", 9050); - GetProxy(NET_IPV4, addrProxy); - CNetAddr addr(value.toString().toStdString()); - addrProxy.SetIP(addr); - settings.setValue("addrProxy", addrProxy.ToStringIPPort().c_str()); - successful = ApplyProxySettings(); - } - break; - case ProxyPort: - { - CService addrProxy("127.0.0.1", 9050); - GetProxy(NET_IPV4, addrProxy); - addrProxy.SetPort(value.toInt()); - settings.setValue("addrProxy", addrProxy.ToStringIPPort().c_str()); - successful = ApplyProxySettings(); - } - break; - case ProxySocksVersion: - settings.setValue("nSocksVersion", value.toInt()); - ApplyProxySettings(); - break; + case ProxyIP: { + proxyType proxy; + proxy.first = CService("127.0.0.1", 9050); + GetProxy(NET_IPV4, proxy); + + CNetAddr addr(value.toString().toStdString()); + proxy.first.SetIP(addr); + settings.setValue("addrProxy", proxy.first.ToStringIPPort().c_str()); + successful = ApplyProxySettings(); + } + break; + case ProxyPort: { + proxyType proxy; + proxy.first = CService("127.0.0.1", 9050); + GetProxy(NET_IPV4, proxy); + + proxy.first.SetPort(value.toInt()); + settings.setValue("addrProxy", proxy.first.ToStringIPPort().c_str()); + successful = ApplyProxySettings(); + } + break; + case ProxySocksVersion: { + proxyType proxy; + proxy.second = 5; + GetProxy(NET_IPV4, proxy); + + proxy.second = value.toInt(); + settings.setValue("nSocksVersion", proxy.second); + successful = ApplyProxySettings(); + } + break; case Fee: nTransactionFee = value.toLongLong(); settings.setValue("nTransactionFee", nTransactionFee); @@ -262,23 +271,3 @@ qint64 OptionsModel::getTransactionFee() { return nTransactionFee; } - -bool OptionsModel::getMinimizeToTray() -{ - return fMinimizeToTray; -} - -bool OptionsModel::getMinimizeOnClose() -{ - return fMinimizeOnClose; -} - -int OptionsModel::getDisplayUnit() -{ - return nDisplayUnit; -} - -bool OptionsModel::getDisplayAddresses() -{ - return bDisplayAddresses; -} diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 34724ad032..2d86a7a9ca 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -44,10 +44,10 @@ public: /* Explicit getters */ qint64 getTransactionFee(); - bool getMinimizeToTray(); - bool getMinimizeOnClose(); - int getDisplayUnit(); - bool getDisplayAddresses(); + bool getMinimizeToTray() { return fMinimizeToTray; } + bool getMinimizeOnClose() { return fMinimizeOnClose; } + int getDisplayUnit() { return nDisplayUnit; } + bool getDisplayAddresses() { return bDisplayAddresses; } QString getLanguage() { return language; } private: diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index efc77e190e..e358c12e96 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -234,8 +234,6 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "<br><b>" + tr("Transaction") + ":</b><br>"; strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); - CTxDB txdb("r"); // To fetch source txouts - strHTML += "<br><b>" + tr("Inputs") + ":</b>"; strHTML += "<ul>"; @@ -245,8 +243,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { COutPoint prevout = txin.prevout; - CTransaction prev; - if(txdb.ReadDiskTx(prevout.hash, prev)) + CCoins prev; + if(pcoinsTip->GetCoins(prevout.hash, prev)) { if (prevout.n < prev.vout.size()) { diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 2dfdf58422..3fde463cd3 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -159,7 +159,69 @@ Value getblock(const Array& params, bool fHelp) return blockToJSON(block, pblockindex); } +Value gettxoutsetinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gettxoutsetinfo\n" + "Returns statistics about the unspent transaction output set."); + + Object ret; + + CCoinsStats stats; + if (pcoinsTip->GetStats(stats)) { + ret.push_back(Pair("bestblock", pcoinsTip->GetBestBlock()->GetBlockHash().GetHex())); + ret.push_back(Pair("transactions", (boost::int64_t)stats.nTransactions)); + ret.push_back(Pair("txouts", (boost::int64_t)stats.nTransactionOutputs)); + ret.push_back(Pair("bytes_serialized", (boost::int64_t)stats.nSerializedSize)); + } + return ret; +} +Value gettxout(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error( + "gettxout <txid> <n> [includemempool=true]\n" + "Returns details about an unspent transaction output."); + Object ret; + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + int n = params[1].get_int(); + bool fMempool = true; + if (params.size() > 2) + fMempool = params[2].get_bool(); + + CCoins coins; + if (fMempool) { + LOCK(mempool.cs); + CCoinsViewMemPool view(*pcoinsTip, mempool); + if (!view.GetCoins(hash, coins)) + return Value::null; + mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool + } else { + if (!pcoinsTip->GetCoins(hash, coins)) + return Value::null; + } + if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) + return Value::null; + + ret.push_back(Pair("bestblock", pcoinsTip->GetBestBlock()->GetBlockHash().GetHex())); + if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) + ret.push_back(Pair("confirmations", 0)); + else + ret.push_back(Pair("confirmations", pcoinsTip->GetBestBlock()->nHeight - coins.nHeight + 1)); + ret.push_back(Pair("amount", (boost::int64_t)coins.vout[n].nValue)); + Object o; + o.push_back(Pair("asm", coins.vout[n].scriptPubKey.ToString())); + o.push_back(Pair("hex", HexStr(coins.vout[n].scriptPubKey.begin(), coins.vout[n].scriptPubKey.end()))); + ret.push_back(Pair("scriptPubKey", o)); + ret.push_back(Pair("version", coins.nVersion)); + ret.push_back(Pair("coinbase", coins.fCoinBase)); + + return ret; +} diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 96712482d5..0270b5f102 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -198,7 +198,7 @@ Value getwork(const Array& params, bool fHelp) Value getblocktemplate(const Array& params, bool fHelp) { - if (fHelp || params.size() > 1) + if (fHelp || params.size() != 1) throw runtime_error( "getblocktemplate [params]\n" "Returns data needed to construct a block to work on:\n" @@ -281,7 +281,7 @@ Value getblocktemplate(const Array& params, bool fHelp) Array transactions; map<uint256, int64_t> setTxIndex; int i = 0; - CTxDB txdb("r"); + CCoinsViewCache &view = *pcoinsTip; BOOST_FOREACH (CTransaction& tx, pblock->vtx) { uint256 txHash = tx.GetHash(); @@ -298,25 +298,21 @@ Value getblocktemplate(const Array& params, bool fHelp) entry.push_back(Pair("hash", txHash.GetHex())); - MapPrevTx mapInputs; - map<uint256, CTxIndex> mapUnused; - bool fInvalid = false; - if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) + Array deps; + BOOST_FOREACH (const CTxIn &in, tx.vin) { - entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut()))); - - Array deps; - BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs) - { - if (setTxIndex.count(inp.first)) - deps.push_back(setTxIndex[inp.first]); - } - entry.push_back(Pair("depends", deps)); + if (setTxIndex.count(in.prevout.hash)) + deps.push_back(setTxIndex[in.prevout.hash]); + } + entry.push_back(Pair("depends", deps)); - int64_t nSigOps = tx.GetLegacySigOpCount(); - nSigOps += tx.GetP2SHSigOpCount(mapInputs); - entry.push_back(Pair("sigops", nSigOps)); + int64_t nSigOps = tx.GetLegacySigOpCount(); + if (tx.HaveInputs(view)) + { + entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(view) - tx.GetValueOut()))); + nSigOps += tx.GetP2SHSigOpCount(view); } + entry.push_back(Pair("sigops", nSigOps)); transactions.push_back(entry); } @@ -364,18 +360,17 @@ Value submitblock(const Array& params, bool fHelp) vector<unsigned char> blockData(ParseHex(params[0].get_str())); CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION); - CBlock block; + CBlock pblock; try { - ssBlock >> block; + ssBlock >> pblock; } catch (std::exception &e) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); } - bool fAccepted = ProcessBlock(NULL, &block); + bool fAccepted = ProcessBlock(NULL, &pblock); if (!fAccepted) return "rejected"; return Value::null; } - diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index e634ed7ddc..72a89c08c1 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -118,7 +118,7 @@ Value getrawtransaction(const Array& params, bool fHelp) CTransaction tx; uint256 hashBlock = 0; - if (!GetTransaction(hash, tx, hashBlock)) + if (!GetTransaction(hash, tx, hashBlock, true)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); @@ -335,26 +335,21 @@ Value signrawtransaction(const Array& params, bool fHelp) bool fComplete = true; // Fetch previous transactions (inputs): - map<COutPoint, CScript> mapPrevOut; - for (unsigned int i = 0; i < mergedTx.vin.size(); i++) + CCoinsView viewDummy; + CCoinsViewCache view(viewDummy); { - CTransaction tempTx; - MapPrevTx mapPrevTx; - CTxDB txdb("r"); - map<uint256, CTxIndex> unused; - bool fInvalid; - - // FetchInputs aborts on failure, so we go one at a time. - tempTx.vin.push_back(mergedTx.vin[i]); - tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid); - - // Copy results into mapPrevOut: - BOOST_FOREACH(const CTxIn& txin, tempTx.vin) - { + LOCK(mempool.cs); + CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(viewChain, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + + BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { const uint256& prevHash = txin.prevout.hash; - if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n) - mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey; + CCoins coins; + view.GetCoins(prevHash, coins); // this is certainly allowed to fail } + + view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long } // Add previous txouts given in the RPC call: @@ -386,20 +381,19 @@ Value signrawtransaction(const Array& params, bool fHelp) vector<unsigned char> pkData(ParseHex(pkHex)); CScript scriptPubKey(pkData.begin(), pkData.end()); - COutPoint outpoint(txid, nOut); - if (mapPrevOut.count(outpoint)) - { - // Complain if scriptPubKey doesn't match - if (mapPrevOut[outpoint] != scriptPubKey) - { + CCoins coins; + if (view.GetCoins(txid, coins)) { + if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) { string err("Previous output scriptPubKey mismatch:\n"); - err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+ + err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ scriptPubKey.ToString(); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); } + // what todo if txid is known, but the actual output isn't? } - else - mapPrevOut[outpoint] = scriptPubKey; + coins.vout[nOut].scriptPubKey = scriptPubKey; + coins.vout[nOut].nValue = 0; // we don't know the actual output value + view.SetCoins(txid, coins); } } @@ -452,12 +446,13 @@ Value signrawtransaction(const Array& params, bool fHelp) for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; - if (mapPrevOut.count(txin.prevout) == 0) + CCoins coins; + if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) { fComplete = false; continue; } - const CScript& prevPubKey = mapPrevOut[txin.prevout]; + const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey; txin.scriptSig.clear(); // Only sign SIGHASH_SINGLE if there's a corresponding output: @@ -469,7 +464,7 @@ Value signrawtransaction(const Array& params, bool fHelp) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0)) + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, true, 0)) fComplete = false; } @@ -505,25 +500,24 @@ Value sendrawtransaction(const Array& params, bool fHelp) } uint256 hashTx = tx.GetHash(); - // See if the transaction is already in a block - // or in the memory pool: - CTransaction existingTx; - uint256 hashBlock = 0; - if (GetTransaction(hashTx, existingTx, hashBlock)) + bool fHave = false; + CCoinsViewCache &view = *pcoinsTip; + CCoins existingCoins; { - if (hashBlock != 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("transaction already in block ")+hashBlock.GetHex()); + fHave = view.GetCoins(hashTx, existingCoins); + if (!fHave) { + // push to local node + if (!tx.AcceptToMemoryPool()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); + } + } + if (fHave) { + if (existingCoins.nHeight < 1000000000) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "transaction already in block chain"); // Not in block, but already in the memory pool; will drop // through to re-relay it. - } - else - { - // push to local node - CTxDB txdb("r"); - if (!tx.AcceptToMemoryPool(txdb)) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); - - SyncWithWallets(tx, NULL, true); + } else { + SyncWithWallets(hashTx, tx, NULL, true); } RelayMessage(CInv(MSG_TX, hashTx), tx); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 51768c9b54..6f8728e979 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -62,8 +62,8 @@ Value getinfo(const Array& params, bool fHelp) "getinfo\n" "Returns an object containing various state info."); - CService addrProxy; - GetProxy(NET_IPV4, addrProxy); + proxyType proxy; + GetProxy(NET_IPV4, proxy); Object obj; obj.push_back(Pair("version", (int)CLIENT_VERSION)); @@ -72,7 +72,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); obj.push_back(Pair("blocks", (int)nBestHeight)); obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (addrProxy.IsValid() ? addrProxy.ToStringIPPort() : string()))); + obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("testnet", fTestNet)); obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); @@ -329,12 +329,12 @@ Value signmessage(const Array& params, bool fHelp) if (!pwalletMain->GetKey(keyID, key)) throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); - CDataStream ss(SER_GETHASH, 0); + CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; vector<unsigned char> vchSig; - if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig)) + if (!key.SignCompact(ss.GetHash(), vchSig)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); return EncodeBase64(&vchSig[0], vchSig.size()); @@ -365,12 +365,12 @@ Value verifymessage(const Array& params, bool fHelp) if (fInvalid) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); - CDataStream ss(SER_GETHASH, 0); + CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; CKey key; - if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig)) + if (!key.SetCompactSignature(ss.GetHash(), vchSig)) return false; return (key.GetPubKey().GetID() == keyID); diff --git a/src/script.cpp b/src/script.cpp index 4357a9a1b3..36d208c247 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -253,7 +253,68 @@ const char* GetOpName(opcodetype opcode) } } -bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) +bool IsCanonicalPubKey(const valtype &vchPubKey) { + if (vchPubKey.size() < 33) + return error("Non-canonical public key: too short"); + if (vchPubKey[0] == 0x04) { + if (vchPubKey.size() != 65) + return error("Non-canonical public key: invalid length for uncompressed key"); + } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { + if (vchPubKey.size() != 33) + return error("Non-canonical public key: invalid length for compressed key"); + } else { + return error("Non-canonical public key: compressed nor uncompressed"); + } + return true; +} + +bool IsCanonicalSignature(const valtype &vchSig) { + // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + // A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype> + // Where R and S are not negative (their first byte has its highest bit not set), and not + // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, + // in which case a single 0 byte is necessary and even required). + if (vchSig.size() < 9) + return error("Non-canonical signature: too short"); + if (vchSig.size() > 73) + return error("Non-canonical signature: too long"); + if (vchSig[vchSig.size() - 1] & 0x7C) + return error("Non-canonical signature: unknown hashtype byte"); + if (vchSig[0] != 0x30) + return error("Non-canonical signature: wrong type"); + if (vchSig[1] != vchSig.size()-3) + return error("Non-canonical signature: wrong length marker"); + unsigned int nLenR = vchSig[3]; + if (5 + nLenR >= vchSig.size()) + return error("Non-canonical signature: S length misplaced"); + unsigned int nLenS = vchSig[5+nLenR]; + if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) + return error("Non-canonical signature: R+S length mismatch"); + + const unsigned char *R = &vchSig[4]; + if (R[-2] != 0x02) + return error("Non-canonical signature: R value type mismatch"); + if (nLenR == 0) + return error("Non-canonical signature: R length is zero"); + if (R[0] & 0x80) + return error("Non-canonical signature: R value negative"); + if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) + return error("Non-canonical signature: R value excessively padded"); + + const unsigned char *S = &vchSig[6+nLenR]; + if (S[-2] != 0x02) + return error("Non-canonical signature: S value type mismatch"); + if (nLenS == 0) + return error("Non-canonical signature: S length is zero"); + if (S[0] & 0x80) + return error("Non-canonical signature: S value negative"); + if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) + return error("Non-canonical signature: S value excessively padded"); + + return true; +} + +bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, bool fStrictEncodings, int nHashType) { CAutoBN_CTX pctx; CScript::const_iterator pc = script.begin(); @@ -944,7 +1005,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(CScript(vchSig)); - bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); + bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); + if (fSuccess) + fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); popstack(stack); popstack(stack); @@ -1004,8 +1067,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co valtype& vchPubKey = stacktop(-ikey); // Check signature - if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType)) - { + bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); + if (fOk) + fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); + + if (fOk) { isig++; nSigsCount--; } @@ -1117,10 +1183,9 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int } // Serialize and hash - CDataStream ss(SER_GETHASH, 0); - ss.reserve(10000); + CHashWriter ss(SER_GETHASH, 0); ss << txTmp << nHashType; - return Hash(ss.begin(), ss.end()); + return ss.GetHash(); } @@ -1572,14 +1637,14 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto } bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, int nHashType) + bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType) { vector<vector<unsigned char> > stack, stackCopy; - if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) + if (!EvalScript(stack, scriptSig, txTo, nIn, fStrictEncodings, nHashType)) return false; if (fValidatePayToScriptHash) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) + if (!EvalScript(stack, scriptPubKey, txTo, nIn, fStrictEncodings, nHashType)) return false; if (stack.empty()) return false; @@ -1597,7 +1662,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stackCopy); - if (!EvalScript(stackCopy, pubKey2, txTo, nIn, nHashType)) + if (!EvalScript(stackCopy, pubKey2, txTo, nIn, fStrictEncodings, nHashType)) return false; if (stackCopy.empty()) return false; @@ -1640,7 +1705,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa } // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, true, 0); + return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, true, true, 0); } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) @@ -1653,7 +1718,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); } -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType) +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType) { assert(nIn < txTo.vin.size()); const CTxIn& txin = txTo.vin[nIn]; @@ -1661,10 +1726,7 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig return false; const CTxOut& txout = txFrom.vout[txin.prevout.n]; - if (txin.prevout.hash != txFrom.GetHash()) - return false; - - return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, nHashType); + return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, fStrictEncodings, nHashType); } static CScript PushAll(const vector<valtype>& values) @@ -1782,9 +1844,9 @@ CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsign Solver(scriptPubKey, txType, vSolutions); vector<valtype> stack1; - EvalScript(stack1, scriptSig1, CTransaction(), 0, 0); + EvalScript(stack1, scriptSig1, CTransaction(), 0, true, 0); vector<valtype> stack2; - EvalScript(stack2, scriptSig2, CTransaction(), 0, 0); + EvalScript(stack2, scriptSig2, CTransaction(), 0, true, 0); return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); } @@ -1885,3 +1947,128 @@ void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys) *this << key.GetPubKey(); *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; } + +bool CScriptCompressor::IsToKeyID(CKeyID &hash) const +{ + if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 + && script[2] == 20 && script[23] == OP_EQUALVERIFY + && script[24] == OP_CHECKSIG) { + memcpy(&hash, &script[3], 20); + return true; + } + return false; +} + +bool CScriptCompressor::IsToScriptID(CScriptID &hash) const +{ + if (script.size() == 23 && script[0] == OP_HASH160 && script[1] == 20 + && script[22] == OP_EQUAL) { + memcpy(&hash, &script[2], 20); + return true; + } + return false; +} + +bool CScriptCompressor::IsToPubKey(std::vector<unsigned char> &pubkey) const +{ + if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG + && (script[1] == 0x02 || script[1] == 0x03)) { + pubkey.resize(33); + memcpy(&pubkey[0], &script[1], 33); + return true; + } + if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG + && script[1] == 0x04) { + pubkey.resize(65); + memcpy(&pubkey[0], &script[1], 65); + CKey key; + return (key.SetPubKey(CPubKey(pubkey))); // SetPubKey fails if this is not a valid public key, a case that would not be compressible + } + return false; +} + +bool CScriptCompressor::Compress(std::vector<unsigned char> &out) const +{ + CKeyID keyID; + if (IsToKeyID(keyID)) { + out.resize(21); + out[0] = 0x00; + memcpy(&out[1], &keyID, 20); + return true; + } + CScriptID scriptID; + if (IsToScriptID(scriptID)) { + out.resize(21); + out[0] = 0x01; + memcpy(&out[1], &scriptID, 20); + return true; + } + std::vector<unsigned char> pubkey; + if (IsToPubKey(pubkey)) { + out.resize(33); + memcpy(&out[1], &pubkey[1], 32); + if (pubkey[0] == 0x02 || pubkey[0] == 0x03) { + out[0] = pubkey[0]; + return true; + } else if (pubkey[0] == 0x04) { + out[0] = 0x04 | (pubkey[64] & 0x01); + return true; + } + } + return false; +} + +unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const +{ + if (nSize == 0 || nSize == 1) + return 20; + if (nSize == 2 || nSize == 3 || nSize == 4 || nSize == 5) + return 32; + return 0; +} + +bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigned char> &in) +{ + switch(nSize) { + case 0x00: + script.resize(25); + script[0] = OP_DUP; + script[1] = OP_HASH160; + script[2] = 20; + memcpy(&script[3], &in[0], 20); + script[23] = OP_EQUALVERIFY; + script[24] = OP_CHECKSIG; + return true; + case 0x01: + script.resize(23); + script[0] = OP_HASH160; + script[1] = 20; + memcpy(&script[2], &in[0], 20); + script[22] = OP_EQUAL; + return true; + case 0x02: + case 0x03: + script.resize(35); + script[0] = 33; + script[1] = nSize; + memcpy(&script[2], &in[0], 32); + script[34] = OP_CHECKSIG; + return true; + case 0x04: + case 0x05: + std::vector<unsigned char> vch(33, 0x00); + vch[0] = nSize - 2; + memcpy(&vch[1], &in[0], 32); + CKey key; + if (!key.SetPubKey(CPubKey(vch))) + return false; + key.SetCompressedPubKey(false); // Decompress public key + CPubKey pubkey = key.GetPubKey(); + script.resize(67); + script[0] = 65; + memcpy(&script[1], &pubkey.Raw()[0], 65); + script[66] = OP_CHECKSIG; + return true; + } + return false; +} diff --git a/src/script.h b/src/script.h index fdcf802945..f9df587ca3 100644 --- a/src/script.h +++ b/src/script.h @@ -14,6 +14,7 @@ #include "keystore.h" #include "bignum.h" +class CCoins; class CTransaction; /** Signature hash types/flags */ @@ -579,11 +580,83 @@ public: } }; +/** Compact serializer for scripts. + * + * It detects common cases and encodes them much more efficiently. + * 3 special cases are defined: + * * Pay to pubkey hash (encoded as 21 bytes) + * * Pay to script hash (encoded as 21 bytes) + * * Pay to pubkey starting with 0x02, 0x03 or 0x04 (encoded as 33 bytes) + * + * Other scripts up to 121 bytes require 1 byte + script length. Above + * that, scripts up to 16505 bytes require 2 bytes + script length. + */ +class CScriptCompressor +{ +private: + // make this static for now (there are only 6 special scripts defined) + // this can potentially be extended together with a new nVersion for + // transactions, in which case this value becomes dependent on nVersion + // and nHeight of the enclosing transaction. + static const unsigned int nSpecialScripts = 6; + + CScript &script; +protected: + // These check for scripts for which a special case with a shorter encoding is defined. + // They are implemented separately from the CScript test, as these test for exact byte + // sequence correspondences, and are more strict. For example, IsToPubKey also verifies + // whether the public key is valid (as invalid ones cannot be represented in compressed + // form). + bool IsToKeyID(CKeyID &hash) const; + bool IsToScriptID(CScriptID &hash) const; + bool IsToPubKey(std::vector<unsigned char> &pubkey) const; + + bool Compress(std::vector<unsigned char> &out) const; + unsigned int GetSpecialSize(unsigned int nSize) const; + bool Decompress(unsigned int nSize, const std::vector<unsigned char> &out); +public: + CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + std::vector<unsigned char> compr; + if (Compress(compr)) + return compr.size(); + unsigned int nSize = script.size() + nSpecialScripts; + return script.size() + VARINT(nSize).GetSerializeSize(nType, nVersion); + } + template<typename Stream> + void Serialize(Stream &s, int nType, int nVersion) const { + std::vector<unsigned char> compr; + if (Compress(compr)) { + s << CFlatData(&compr[0], &compr[compr.size()]); + return; + } + unsigned int nSize = script.size() + nSpecialScripts; + s << VARINT(nSize); + s << CFlatData(&script[0], &script[script.size()]); + } + template<typename Stream> + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nSize; + s >> VARINT(nSize); + if (nSize < nSpecialScripts) { + std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00); + s >> REF(CFlatData(&vch[0], &vch[vch.size()])); + Decompress(nSize, vch); + return; + } + nSize -= nSpecialScripts; + script.resize(nSize); + s >> REF(CFlatData(&script[0], &script[script.size()])); + } +}; +bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey); +bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig); -bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType); +bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, bool fStrictEncodings, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); bool IsStandard(const CScript& scriptPubKey); @@ -594,8 +667,8 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, int nHashType); -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType); + bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); // Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, // combine them intelligently and return the result. diff --git a/src/serialize.h b/src/serialize.h index 63df3160a6..549e46cbc3 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -238,9 +238,75 @@ uint64 ReadCompactSize(Stream& is) return nSizeRet; } +// Variable-length integers: bytes are a MSB base-128 encoding of the number. +// The high bit in each byte signifies whether another digit follows. To make +// the encoding is one-to-one, one is subtracted from all but the last digit. +// Thus, the byte sequence a[] with length len, where all but the last byte +// has bit 128 set, encodes the number: +// +// (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1)) +// +// Properties: +// * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes) +// * Every integer has exactly one encoding +// * Encoding does not depend on size of original integer type +// * No redundancy: every (infinite) byte sequence corresponds to a list +// of encoded integers. +// +// 0: [0x00] 256: [0x81 0x00] +// 1: [0x01] 16383: [0xFE 0x7F] +// 127: [0x7F] 16384: [0xFF 0x00] +// 128: [0x80 0x00] 16511: [0x80 0xFF 0x7F] +// 255: [0x80 0x7F] 65535: [0x82 0xFD 0x7F] +// 2^32: [0x8E 0xFE 0xFE 0xFF 0x00] + +template<typename I> +inline unsigned int GetSizeOfVarInt(I n) +{ + int nRet = 0; + while(true) { + nRet++; + if (n <= 0x7F) + break; + n = (n >> 7) - 1; + } + return nRet; +} + +template<typename Stream, typename I> +void WriteVarInt(Stream& os, I n) +{ + unsigned char tmp[(sizeof(n)*8+6)/7]; + int len=0; + while(true) { + tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00); + if (n <= 0x7F) + break; + n = (n >> 7) - 1; + len++; + } + do { + WRITEDATA(os, tmp[len]); + } while(len--); +} +template<typename Stream, typename I> +I ReadVarInt(Stream& is) +{ + I n = 0; + while(true) { + unsigned char chData; + READDATA(is, chData); + n = (n << 7) | (chData & 0x7F); + if (chData & 0x80) + n++; + else + return n; + } +} -#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) +#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) +#define VARINT(obj) REF(WrapVarInt(REF(obj))) /** Wrapper for serializing arrays and POD. */ @@ -274,6 +340,32 @@ public: } }; +template<typename I> +class CVarInt +{ +protected: + I &n; +public: + CVarInt(I& nIn) : n(nIn) { } + + unsigned int GetSerializeSize(int, int) const { + return GetSizeOfVarInt<I>(n); + } + + template<typename Stream> + void Serialize(Stream &s, int, int) const { + WriteVarInt<Stream,I>(s, n); + } + + template<typename Stream> + void Unserialize(Stream& s, int, int) { + n = ReadVarInt<Stream,I>(s); + } +}; + +template<typename I> +CVarInt<I> WrapVarInt(I& n) { return CVarInt<I>(n); } + // // Forward declarations // diff --git a/src/strlcpy.h b/src/strlcpy.h deleted file mode 100644 index 2cc786e953..0000000000 --- a/src/strlcpy.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#ifndef BITCOIN_STRLCPY_H -#define BITCOIN_STRLCPY_H - -#include <stdlib.h> -#include <string.h> - -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -inline size_t strlcpy(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0) - { - while (--n != 0) - { - if ((*d++ = *s++) == '\0') - break; - } - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) - { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -} - -/* - * Appends src to string dst of size siz (unlike strncat, siz is the - * full size of dst, not space left). At most siz-1 characters - * will be copied. Always NUL terminates (unless siz <= strlen(dst)). - * Returns strlen(src) + MIN(siz, strlen(initial dst)). - * If retval >= siz, truncation occurred. - */ -inline size_t strlcat(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') - { - if (n != 1) - { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(dlen + (s - src)); /* count does not include NUL */ -} -#endif diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 7f08dd5543..8235b0bda7 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) mst1 = boost::posix_time::microsec_clock::local_time(); for (unsigned int i = 0; i < 5; i++) for (unsigned int j = 0; j < tx.vin.size(); j++) - BOOST_CHECK(VerifySignature(orphans[j], tx, j, true, SIGHASH_ALL)); + BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, true, true, SIGHASH_ALL)); mst2 = boost::posix_time::microsec_clock::local_time(); msdiff = mst2 - mst1; long nManyValidate = msdiff.total_milliseconds(); @@ -289,13 +289,13 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) // Empty a signature, validation should fail: CScript save = tx.vin[0].scriptSig; tx.vin[0].scriptSig = CScript(); - BOOST_CHECK(!VerifySignature(orphans[0], tx, 0, true, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, true, true, SIGHASH_ALL)); tx.vin[0].scriptSig = save; // Swap signatures, validation should fail: std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); - BOOST_CHECK(!VerifySignature(orphans[0], tx, 0, true, SIGHASH_ALL)); - BOOST_CHECK(!VerifySignature(orphans[1], tx, 1, true, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, true, true, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, true, true, SIGHASH_ALL)); std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); // Exercise -maxsigcachesize code: @@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0)); BOOST_CHECK(tx.vin[0].scriptSig != oldSig); for (unsigned int j = 0; j < tx.vin.size(); j++) - BOOST_CHECK(VerifySignature(orphans[j], tx, j, true, SIGHASH_ALL)); + BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, true, true, SIGHASH_ALL)); mapArgs.erase("-maxsigcachesize"); LimitOrphanTxSize(0); diff --git a/src/test/canonical_tests.cpp b/src/test/canonical_tests.cpp new file mode 100644 index 0000000000..42d21f8ac5 --- /dev/null +++ b/src/test/canonical_tests.cpp @@ -0,0 +1,87 @@ +// +// Unit tests for canonical signatures + +#include "json/json_spirit_writer_template.h" +#include <boost/test/unit_test.hpp> +#include <openssl/ecdsa.h> + +#include "key.h" +#include "script.h" +#include "util.h" + +using namespace std; +using namespace json_spirit; + + +// In script_tests.cpp +extern Array read_json(const std::string& filename); + +BOOST_AUTO_TEST_SUITE(canonical_tests) + +// OpenSSL-based test for canonical signature (without test for hashtype byte) +bool static IsCanonicalSignature_OpenSSL_inner(const std::vector<unsigned char>& vchSig) +{ + if (vchSig.size() == 0) + return false; + const unsigned char *input = &vchSig[0]; + ECDSA_SIG *psig = NULL; + d2i_ECDSA_SIG(&psig, &input, vchSig.size()); + if (psig == NULL) + return false; + unsigned char buf[256]; + unsigned char *pbuf = buf; + unsigned int nLen = i2d_ECDSA_SIG(psig, NULL); + if (nLen != vchSig.size()) { + ECDSA_SIG_free(psig); + return false; + } + nLen = i2d_ECDSA_SIG(psig, &pbuf); + ECDSA_SIG_free(psig); + return (memcmp(&vchSig[0], &buf[0], nLen) == 0); +} + +// OpenSSL-based test for canonical signature +bool static IsCanonicalSignature_OpenSSL(const std::vector<unsigned char> &vchSignature) { + if (vchSignature.size() < 1) + return false; + if (vchSignature.size() > 127) + return false; + if (vchSignature[vchSignature.size() - 1] & 0x7C) + return false; + + std::vector<unsigned char> vchSig(vchSignature); + vchSig.pop_back(); + if (!IsCanonicalSignature_OpenSSL_inner(vchSig)) + return false; + return true; +} + +BOOST_AUTO_TEST_CASE(script_canon) +{ + Array tests = read_json("sig_canonical.json"); + + BOOST_FOREACH(Value &tv, tests) { + string test = tv.get_str(); + if (IsHex(test)) { + std::vector<unsigned char> sig = ParseHex(test); + BOOST_CHECK_MESSAGE(IsCanonicalSignature(sig), test); + BOOST_CHECK_MESSAGE(IsCanonicalSignature_OpenSSL(sig), test); + } + } +} + +BOOST_AUTO_TEST_CASE(script_noncanon) +{ + Array tests = read_json("sig_noncanonical.json"); + + BOOST_FOREACH(Value &tv, tests) { + string test = tv.get_str(); + if (IsHex(test)) { + std::vector<unsigned char> sig = ParseHex(test); + BOOST_CHECK_MESSAGE(!IsCanonicalSignature(sig), test); + BOOST_CHECK_MESSAGE(!IsCanonicalSignature_OpenSSL(sig), test); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp new file mode 100644 index 0000000000..71b86bcb41 --- /dev/null +++ b/src/test/compress_tests.cpp @@ -0,0 +1,62 @@ +#include <boost/test/unit_test.hpp> + +#include <string> +#include <vector> + +#include "main.h" + +// amounts 0.00000001 .. 0.00100000 +#define NUM_MULTIPLES_UNIT 100000 + +// amounts 0.01 .. 100.00 +#define NUM_MULTIPLES_CENT 10000 + +// amounts 1 .. 10000 +#define NUM_MULTIPLES_1BTC 10000 + +// amounts 50 .. 21000000 +#define NUM_MULTIPLES_50BTC 420000 + +using namespace std; + +BOOST_AUTO_TEST_SUITE(compress_tests) + +bool static TestEncode(uint64 in) { + return in == CTxOutCompressor::DecompressAmount(CTxOutCompressor::CompressAmount(in)); +} + +bool static TestDecode(uint64 in) { + return in == CTxOutCompressor::CompressAmount(CTxOutCompressor::DecompressAmount(in)); +} + +bool static TestPair(uint64 dec, uint64 enc) { + return CTxOutCompressor::CompressAmount(dec) == enc && + CTxOutCompressor::DecompressAmount(enc) == dec; +} + +BOOST_AUTO_TEST_CASE(compress_amounts) +{ + BOOST_CHECK(TestPair( 0, 0x0)); + BOOST_CHECK(TestPair( 1, 0x1)); + BOOST_CHECK(TestPair( CENT, 0x7)); + BOOST_CHECK(TestPair( COIN, 0x9)); + BOOST_CHECK(TestPair( 50*COIN, 0x32)); + BOOST_CHECK(TestPair(21000000*COIN, 0x1406f40)); + + for (uint64 i = 1; i <= NUM_MULTIPLES_UNIT; i++) + BOOST_CHECK(TestEncode(i)); + + for (uint64 i = 1; i <= NUM_MULTIPLES_CENT; i++) + BOOST_CHECK(TestEncode(i * CENT)); + + for (uint64 i = 1; i <= NUM_MULTIPLES_1BTC; i++) + BOOST_CHECK(TestEncode(i * COIN)); + + for (uint64 i = 1; i <= NUM_MULTIPLES_50BTC; i++) + BOOST_CHECK(TestEncode(i * 50 * COIN)); + + for (uint64 i = 0; i < 100000; i++) + BOOST_CHECK(TestDecode(i)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/data/sig_canonical.json b/src/test/data/sig_canonical.json new file mode 100644 index 0000000000..e43a08629a --- /dev/null +++ b/src/test/data/sig_canonical.json @@ -0,0 +1,7 @@ +[ + "300602010002010001", + "3008020200ff020200ff01", + "304402203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df0c022030b61dd36543125d56b9f9f3a1f9353189e5af33cdda8d77a5209aec03978fa001", + "30450220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01", + "3046022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01" +] diff --git a/src/test/data/sig_noncanonical.json b/src/test/data/sig_noncanonical.json new file mode 100644 index 0000000000..d9a6c1cdd8 --- /dev/null +++ b/src/test/data/sig_noncanonical.json @@ -0,0 +1,22 @@ +[ + "non-hex strings are ignored", + + "too short:", "30050201FF020001", + "too long:", "30470221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "hashtype:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed11", + "type:", "314402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "total length:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "S len oob:", "301F01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb101", + "R+S:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed0001", + + "R type:", "304401205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "R len = 0:", "3024020002202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "R<0:", "304402208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "R padded:", "30450221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + + + "S type:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610501202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "S len = 0:", "302402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105020001", + "S<0:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "S padded:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01" +] diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 6bc5e3b99e..aa6feb7201 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -21,7 +21,7 @@ typedef vector<unsigned char> valtype; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, int nHashType); + bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); BOOST_AUTO_TEST_SUITE(multisig_tests) @@ -80,19 +80,19 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.clear(); keys += key[0],key[1]; // magic operator+= from boost.assign s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, true, 0)); + BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, true, true, 0)); for (int i = 0; i < 4; i++) { keys.clear(); keys += key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, 0), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, true, 0), strprintf("a&b 1: %d", i)); keys.clear(); keys += key[1],key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, 0), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, true, 0), strprintf("a&b 2: %d", i)); } // Test a OR b: @@ -102,16 +102,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i]; s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, true, 0), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0), strprintf("a|b: %d", i)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, true, 0), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0), strprintf("a|b: %d", i)); } s.clear(); s << OP_0 << OP_0; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, true, 0)); + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0)); s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, true, 0)); + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, true, true, 0)); for (int i = 0; i < 4; i++) @@ -121,9 +121,9 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i],key[j]; s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, true, 0), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, true, true, 0), strprintf("escrow 1: %d %d", i, j)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, true, 0), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, true, true, 0), strprintf("escrow 2: %d %d", i, j)); } } diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index eabfcd0304..35069a3fdd 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -14,7 +14,7 @@ using namespace std; // Test routines internal to script.cpp: extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, int nHashType); + bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); // Helpers: static std::vector<unsigned char> @@ -40,7 +40,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, txTo, 0, fStrict, 0); + return VerifyScript(scriptSig, scriptPubKey, txTo, 0, fStrict, true, 0); } @@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(sign) { CScript sigSave = txTo[i].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; - bool sigOK = VerifySignature(txFrom, txTo[i], 0, true, 0); + bool sigOK = VerifySignature(CCoins(txFrom, 0), txTo[i], 0, true, true, 0); if (i == j) BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); else @@ -243,7 +243,8 @@ BOOST_AUTO_TEST_CASE(switchover) BOOST_AUTO_TEST_CASE(AreInputsStandard) { - std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs; + CCoinsView coinsDummy; + CCoinsViewCache coins(coinsDummy); CBasicKeyStore keystore; CKey key[3]; vector<CKey> keys; @@ -264,23 +265,29 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) CScript pay1of3; pay1of3.SetMultisig(1, keys); txFrom.vout[0].scriptPubKey = payScriptHash1; + txFrom.vout[0].nValue = 1000; txFrom.vout[1].scriptPubKey = pay1; + txFrom.vout[1].nValue = 2000; txFrom.vout[2].scriptPubKey = pay1of3; + txFrom.vout[2].nValue = 3000; // Last three non-standard: CScript empty; keystore.AddCScript(empty); txFrom.vout[3].scriptPubKey = empty; + txFrom.vout[3].nValue = 4000; // Can't use SetPayToScriptHash, it checks for the empty Script. So: txFrom.vout[4].scriptPubKey << OP_HASH160 << Hash160(empty) << OP_EQUAL; + txFrom.vout[4].nValue = 5000; CScript oneOfEleven; oneOfEleven << OP_1; for (int i = 0; i < 11; i++) oneOfEleven << key[0].GetPubKey(); oneOfEleven << OP_11 << OP_CHECKMULTISIG; txFrom.vout[5].scriptPubKey.SetDestination(oneOfEleven.GetID()); + txFrom.vout[5].nValue = 6000; - mapInputs[txFrom.GetHash()] = make_pair(CTxIndex(), txFrom); + coins.SetCoins(txFrom.GetHash(), CCoins(txFrom, 0)); CTransaction txTo; txTo.vout.resize(1); @@ -297,21 +304,22 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txTo.vin[2].prevout.hash = txFrom.GetHash(); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); - BOOST_CHECK(txTo.AreInputsStandard(mapInputs)); - BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(mapInputs), 1); + BOOST_CHECK(txTo.AreInputsStandard(coins)); + BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(coins), 1); // Make sure adding crap to the scriptSigs makes them non-standard: for (int i = 0; i < 3; i++) { CScript t = txTo.vin[i].scriptSig; txTo.vin[i].scriptSig = (CScript() << 11) + t; - BOOST_CHECK(!txTo.AreInputsStandard(mapInputs)); + BOOST_CHECK(!txTo.AreInputsStandard(coins)); txTo.vin[i].scriptSig = t; } CTransaction txToNonStd; txToNonStd.vout.resize(1); txToNonStd.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID()); + txToNonStd.vout[0].nValue = 1000; txToNonStd.vin.resize(2); txToNonStd.vin[0].prevout.n = 4; txToNonStd.vin[0].prevout.hash = txFrom.GetHash(); @@ -320,11 +328,11 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd.vin[1].prevout.hash = txFrom.GetHash(); txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven); - BOOST_CHECK(!txToNonStd.AreInputsStandard(mapInputs)); - BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(mapInputs), 11); + BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); + BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(coins), 11); txToNonStd.vin[0].scriptSig.clear(); - BOOST_CHECK(!txToNonStd.AreInputsStandard(mapInputs)); + BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 61d9a64eeb..f8e4fa8a49 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -21,7 +21,7 @@ using namespace boost::algorithm; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, int nHashType); + bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType); CScript ParseScript(string s) @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(script_valid) CScript scriptPubKey = ParseScript(scriptPubKeyString); CTransaction tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, true, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, true, true, SIGHASH_NONE), strTest); } } @@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(script_invalid) CScript scriptPubKey = ParseScript(scriptPubKeyString); CTransaction tx; - BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, true, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, true, true, SIGHASH_NONE), strTest); } } @@ -181,18 +181,18 @@ BOOST_AUTO_TEST_CASE(script_PushData) static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a }; vector<vector<unsigned char> > directStack; - BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0)); + BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, true, 0)); vector<vector<unsigned char> > pushdata1Stack; - BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0)); + BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, true, 0)); BOOST_CHECK(pushdata1Stack == directStack); vector<vector<unsigned char> > pushdata2Stack; - BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0)); + BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, true, 0)); BOOST_CHECK(pushdata2Stack == directStack); vector<vector<unsigned char> > pushdata4Stack; - BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0)); + BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, true, 0)); BOOST_CHECK(pushdata4Stack == directStack); } @@ -250,15 +250,15 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) txTo12.vout[0].nValue = 1; CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, true, 0)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, 0)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, true, 0)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, true, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, true, true, 0)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, true, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, true, true, 0)); } BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) @@ -286,46 +286,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector<CKey> keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, true, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, true, true, 0)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, true, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, true, true, 0)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, true, 0)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, true, true, 0)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, true, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, true, true, 0)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, true, 0)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, true, true, 0)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, true, 0)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, true, true, 0)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, true, 0)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, true, true, 0)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, true, 0)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, true, true, 0)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, true, 0)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, true, true, 0)); } BOOST_AUTO_TEST_CASE(script_combineSigs) diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp new file mode 100644 index 0000000000..90ac89f8c5 --- /dev/null +++ b/src/test/serialize_tests.cpp @@ -0,0 +1,45 @@ +#include <boost/test/unit_test.hpp> + +#include <string> +#include <vector> + +#include "serialize.h" + +using namespace std; + +BOOST_AUTO_TEST_SUITE(serialize_tests) + +BOOST_AUTO_TEST_CASE(varints) +{ + // encode + + CDataStream ss(SER_DISK, 0); + CDataStream::size_type size = 0; + for (int i = 0; i < 100000; i++) { + ss << VARINT(i); + size += ::GetSerializeSize(VARINT(i), 0, 0); + BOOST_CHECK(size == ss.size()); + } + + for (uint64 i = 0; i < 100000000000ULL; i += 999999937) { + ss << VARINT(i); + size += ::GetSerializeSize(VARINT(i), 0, 0); + BOOST_CHECK(size == ss.size()); + } + + // decode + for (int i = 0; i < 100000; i++) { + int j; + ss >> VARINT(j); + BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); + } + + for (uint64 i = 0; i < 100000000000ULL; i += 999999937) { + uint64 j; + ss >> VARINT(j); + BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); + } + +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index bcf0907871..c1f47f786b 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -2,6 +2,7 @@ #include <boost/test/unit_test.hpp> #include "db.h" +#include "txdb.h" #include "main.h" #include "wallet.h" @@ -12,10 +13,15 @@ extern bool fPrintToConsole; extern void noui_connect(); struct TestingSetup { + CCoinsViewDB *pcoinsdbview; + TestingSetup() { fPrintToDebugger = true; // don't want to write to debug.log file noui_connect(); bitdb.MakeMock(); + pblocktree = new CBlockTreeDB(true); + pcoinsdbview = new CCoinsViewDB(true); + pcoinsTip = new CCoinsViewCache(*pcoinsdbview); LoadBlockIndex(true); bool fFirstRun; pwalletMain = new CWallet("wallet.dat"); @@ -26,6 +32,9 @@ struct TestingSetup { { delete pwalletMain; pwalletMain = NULL; + delete pcoinsTip; + delete pcoinsdbview; + delete pblocktree; bitdb.Flush(true); } }; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index c230458866..4385b9ba37 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) break; } - BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool(), 0), strTest); + BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool(), false, 0), strTest); } } } @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) break; } - fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool(), 0); + fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool(), true, 0); } BOOST_CHECK_MESSAGE(!fValid, strTest); @@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) // paid to a TX_PUBKEYHASH. // static std::vector<CTransaction> -SetupDummyInputs(CBasicKeyStore& keystoreRet, MapPrevTx& inputsRet) +SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet) { std::vector<CTransaction> dummyTransactions; dummyTransactions.resize(2); @@ -192,14 +192,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, MapPrevTx& inputsRet) dummyTransactions[0].vout[0].scriptPubKey << key[0].GetPubKey() << OP_CHECKSIG; dummyTransactions[0].vout[1].nValue = 50*CENT; dummyTransactions[0].vout[1].scriptPubKey << key[1].GetPubKey() << OP_CHECKSIG; - inputsRet[dummyTransactions[0].GetHash()] = make_pair(CTxIndex(), dummyTransactions[0]); + coinsRet.SetCoins(dummyTransactions[0].GetHash(), CCoins(dummyTransactions[0], 0)); dummyTransactions[1].vout.resize(2); dummyTransactions[1].vout[0].nValue = 21*CENT; dummyTransactions[1].vout[0].scriptPubKey.SetDestination(key[2].GetPubKey().GetID()); dummyTransactions[1].vout[1].nValue = 22*CENT; dummyTransactions[1].vout[1].scriptPubKey.SetDestination(key[3].GetPubKey().GetID()); - inputsRet[dummyTransactions[1].GetHash()] = make_pair(CTxIndex(), dummyTransactions[1]); + coinsRet.SetCoins(dummyTransactions[1].GetHash(), CCoins(dummyTransactions[1], 0)); return dummyTransactions; } @@ -207,8 +207,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, MapPrevTx& inputsRet) BOOST_AUTO_TEST_CASE(test_Get) { CBasicKeyStore keystore; - MapPrevTx dummyInputs; - std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, dummyInputs); + CCoinsView coinsDummy; + CCoinsViewCache coins(coinsDummy); + std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); CTransaction t1; t1.vin.resize(3); @@ -225,25 +226,24 @@ BOOST_AUTO_TEST_CASE(test_Get) t1.vout[0].nValue = 90*CENT; t1.vout[0].scriptPubKey << OP_1; - BOOST_CHECK(t1.AreInputsStandard(dummyInputs)); - BOOST_CHECK_EQUAL(t1.GetValueIn(dummyInputs), (50+21+22)*CENT); + BOOST_CHECK(t1.AreInputsStandard(coins)); + BOOST_CHECK_EQUAL(t1.GetValueIn(coins), (50+21+22)*CENT); // Adding extra junk to the scriptSig should make it non-standard: t1.vin[0].scriptSig << OP_11; - BOOST_CHECK(!t1.AreInputsStandard(dummyInputs)); + BOOST_CHECK(!t1.AreInputsStandard(coins)); // ... as should not having enough: t1.vin[0].scriptSig = CScript(); - BOOST_CHECK(!t1.AreInputsStandard(dummyInputs)); + BOOST_CHECK(!t1.AreInputsStandard(coins)); } BOOST_AUTO_TEST_CASE(test_GetThrow) { CBasicKeyStore keystore; - MapPrevTx dummyInputs; - std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, dummyInputs); - - MapPrevTx missingInputs; + CCoinsView coinsDummy; + CCoinsViewCache coins(coinsDummy); + std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); CTransaction t1; t1.vin.resize(3); @@ -256,9 +256,6 @@ BOOST_AUTO_TEST_CASE(test_GetThrow) t1.vout.resize(2); t1.vout[0].nValue = 90*CENT; t1.vout[0].scriptPubKey << OP_1; - - BOOST_CHECK_THROW(t1.AreInputsStandard(missingInputs), runtime_error); - BOOST_CHECK_THROW(t1.GetValueIn(missingInputs), runtime_error); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/txdb.cpp b/src/txdb.cpp new file mode 100644 index 0000000000..67d15cb58f --- /dev/null +++ b/src/txdb.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "txdb.h" +#include "main.h" + +using namespace std; + +void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) { + if (coins.IsPruned()) + batch.Erase(make_pair('c', hash)); + else + batch.Write(make_pair('c', hash), coins); +} + +void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { + batch.Write('B', hash); +} + +CCoinsViewDB::CCoinsViewDB(bool fMemory) : db(GetDataDir() / "coins", fMemory) { +} + +bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { + return db.Read(make_pair('c', txid), coins); +} + +bool CCoinsViewDB::SetCoins(uint256 txid, const CCoins &coins) { + CLevelDBBatch batch; + BatchWriteCoins(batch, txid, coins); + return db.WriteBatch(batch); +} + +bool CCoinsViewDB::HaveCoins(uint256 txid) { + return db.Exists(make_pair('c', txid)); +} + +CBlockIndex *CCoinsViewDB::GetBestBlock() { + uint256 hashBestChain; + if (!db.Read('B', hashBestChain)) + return NULL; + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hashBestChain); + if (it == mapBlockIndex.end()) + return NULL; + return it->second; +} + +bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { + CLevelDBBatch batch; + BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + return db.WriteBatch(batch); +} + +bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { + printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); + + CLevelDBBatch batch; + for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) + BatchWriteCoins(batch, it->first, it->second); + BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + + return db.WriteBatch(batch); +} + +CBlockTreeDB::CBlockTreeDB(bool fMemory) : CLevelDB(GetDataDir() / "blktree", fMemory) { +} + +bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) +{ + return Write(make_pair('b', blockindex.GetBlockHash()), blockindex); +} + +bool CBlockTreeDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) +{ + return Read('I', bnBestInvalidWork); +} + +bool CBlockTreeDB::WriteBestInvalidWork(const CBigNum& bnBestInvalidWork) +{ + return Write('I', bnBestInvalidWork); +} + +bool CBlockTreeDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) { + return Write(make_pair('f', nFile), info); +} + +bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { + return Read(make_pair('f', nFile), info); +} + +bool CBlockTreeDB::WriteLastBlockFile(int nFile) { + return Write('l', nFile); +} + +bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { + return Read('l', nFile); +} + +bool CCoinsViewDB::GetStats(CCoinsStats &stats) { + leveldb::Iterator *pcursor = db.NewIterator(); + pcursor->SeekToFirst(); + + while (pcursor->Valid()) { + try { + leveldb::Slice slKey = pcursor->key(); + CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); + char chType; + ssKey >> chType; + if (chType == 'c' && !fRequestShutdown) { + leveldb::Slice slValue = pcursor->value(); + CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); + CCoins coins; + ssValue >> coins; + uint256 txhash; + ssKey >> txhash; + + stats.nTransactions++; + BOOST_FOREACH(const CTxOut &out, coins.vout) { + if (!out.IsNull()) + stats.nTransactionOutputs++; + } + stats.nSerializedSize += 32 + slValue.size(); + } + pcursor->Next(); + } catch (std::exception &e) { + return error("%s() : deserialize error", __PRETTY_FUNCTION__); + } + } + delete pcursor; + stats.nHeight = GetBestBlock()->nHeight; + return true; +} + +bool CBlockTreeDB::LoadBlockIndexGuts() +{ + leveldb::Iterator *pcursor = NewIterator(); + + CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); + ssKeySet << make_pair('b', uint256(0)); + pcursor->Seek(ssKeySet.str()); + + // Load mapBlockIndex + while (pcursor->Valid()) { + try { + leveldb::Slice slKey = pcursor->key(); + CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); + char chType; + ssKey >> chType; + if (chType == 'b' && !fRequestShutdown) { + leveldb::Slice slValue = pcursor->value(); + CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); + CDiskBlockIndex diskindex; + ssValue >> diskindex; + + // Construct block index object + CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); + pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); + pindexNew->nHeight = diskindex.nHeight; + pindexNew->nFile = diskindex.nFile; + pindexNew->nDataPos = diskindex.nDataPos; + pindexNew->nUndoPos = diskindex.nUndoPos; + pindexNew->nVersion = diskindex.nVersion; + pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; + pindexNew->nTime = diskindex.nTime; + pindexNew->nBits = diskindex.nBits; + pindexNew->nNonce = diskindex.nNonce; + pindexNew->nStatus = diskindex.nStatus; + pindexNew->nTx = diskindex.nTx; + + // Watch for genesis block + if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) + pindexGenesisBlock = pindexNew; + + if (!pindexNew->CheckIndex()) + return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str()); + + pcursor->Next(); + } else { + break; // if shutdown requested or finished loading block index + } + } catch (std::exception &e) { + return error("%s() : deserialize error", __PRETTY_FUNCTION__); + } + } + delete pcursor; + + return true; +} diff --git a/src/txdb.h b/src/txdb.h new file mode 100644 index 0000000000..123ec00d23 --- /dev/null +++ b/src/txdb.h @@ -0,0 +1,47 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_TXDB_LEVELDB_H +#define BITCOIN_TXDB_LEVELDB_H + +#include "main.h" +#include "leveldb.h" + +/** CCoinsView backed by the LevelDB coin database (coins/) */ +class CCoinsViewDB : public CCoinsView +{ +protected: + CLevelDB db; +public: + CCoinsViewDB(bool fMemory = false); + + bool GetCoins(uint256 txid, CCoins &coins); + bool SetCoins(uint256 txid, const CCoins &coins); + bool HaveCoins(uint256 txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + bool GetStats(CCoinsStats &stats); +}; + +/** Access to the block database (blktree/) */ +class CBlockTreeDB : public CLevelDB +{ +public: + CBlockTreeDB(bool fMemory = false); +private: + CBlockTreeDB(const CBlockTreeDB&); + void operator=(const CBlockTreeDB&); +public: + bool WriteBlockIndex(const CDiskBlockIndex& blockindex); + bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); + bool WriteBestInvalidWork(const CBigNum& bnBestInvalidWork); + bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); + bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo); + bool ReadLastBlockFile(int &nFile); + bool WriteLastBlockFile(int nFile); + bool LoadBlockIndexGuts(); +}; + +#endif // BITCOIN_TXDB_LEVELDB_H diff --git a/src/util.cpp b/src/util.cpp index 296842acc3..9162886450 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -5,10 +5,11 @@ #include "util.h" #include "sync.h" -#include "strlcpy.h" #include "version.h" #include "ui_interface.h" #include <boost/algorithm/string/join.hpp> +#include <boost/algorithm/string/case_conv.hpp> // for to_lower() +#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith() // Work around clang compilation problem in Boost 1.46: // /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup @@ -220,8 +221,14 @@ inline int OutputDebugStringF(const char* pszFormat, ...) if (fileout) { static bool fStartedNewLine = true; - static boost::mutex mutexDebugLog; - boost::mutex::scoped_lock scoped_lock(mutexDebugLog); + + // This routine may be called by global destructors during shutdown. + // Since the order of destruction of static/global objects is undefined, + // allocate mutexDebugLog on the heap the first time this routine + // is called to avoid crashes during shutdown. + static boost::mutex* mutexDebugLog = NULL; + if (mutexDebugLog == NULL) mutexDebugLog = new boost::mutex(); + boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); // reopen the log file, if requested if (fReopenDebugLog) { @@ -499,24 +506,24 @@ void ParseParameters(int argc, const char* const argv[]) mapMultiArgs.clear(); for (int i = 1; i < argc; i++) { - char psz[10000]; - strlcpy(psz, argv[i], sizeof(psz)); - char* pszValue = (char*)""; - if (strchr(psz, '=')) + std::string str(argv[i]); + std::string strValue; + size_t is_index = str.find('='); + if (is_index != std::string::npos) { - pszValue = strchr(psz, '='); - *pszValue++ = '\0'; + strValue = str.substr(is_index+1); + str = str.substr(0, is_index); } - #ifdef WIN32 - _strlwr(psz); - if (psz[0] == '/') - psz[0] = '-'; - #endif - if (psz[0] != '-') +#ifdef WIN32 + boost::to_lower(str); + if (boost::algorithm::starts_with(str, "/")) + str = "-" + str.substr(1); +#endif + if (str[0] != '-') break; - mapArgs[psz] = pszValue; - mapMultiArgs[psz].push_back(pszValue); + mapArgs[str] = strValue; + mapMultiArgs[str].push_back(strValue); } // New 0.6 features: @@ -1112,7 +1119,11 @@ void FileCommit(FILE *fileout) #ifdef WIN32 _commit(_fileno(fileout)); #else + #if defined(__linux__) || defined(__NetBSD__) + fdatasync(fileno(fileout)); + #else fsync(fileno(fileout)); + #endif #endif } @@ -1126,6 +1137,20 @@ int GetFilesize(FILE* file) return nFilesize; } +// this function tries to make a particular range of a file allocated (corresponding to disk space) +// it is advisory, and the range specified in the arguments will never contain live data +void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { + static const char buf[65536] = {}; + fseek(file, offset, SEEK_SET); + while (length > 0) { + unsigned int now = 65536; + if (length < now) + now = length; + fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway + length -= now; + } +} + void ShrinkDebugFile() { // Scroll debug.log if it's getting too big diff --git a/src/util.h b/src/util.h index d80b3c28b2..b798f60aa7 100644 --- a/src/util.h +++ b/src/util.h @@ -196,6 +196,7 @@ bool WildcardMatch(const char* psz, const char* mask); bool WildcardMatch(const std::string& str, const std::string& mask); void FileCommit(FILE *fileout); int GetFilesize(FILE* file); +void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest); boost::filesystem::path GetDefaultDataDir(); const boost::filesystem::path &GetDataDir(bool fNetSpecific = true); diff --git a/src/wallet.cpp b/src/wallet.cpp index f3e71f59aa..1498ff28b7 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -335,7 +335,9 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx) if (mi != mapWallet.end()) { CWalletTx& wtx = (*mi).second; - if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) + if (txin.prevout.n >= wtx.vout.size()) + printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str()); + else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) { printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkSpent(txin.prevout.n); @@ -477,9 +479,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) // Add a transaction to the wallet, or update it. // pblock is optional, but should be provided if the transaction is known to be in a block. // If fUpdate is true, existing transactions will be updated. -bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) +bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) { - uint256 hash = tx.GetHash(); { LOCK(cs_wallet); bool fExisted = mapWallet.count(hash); @@ -683,7 +684,7 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nReceived, } } -void CWalletTx::AddSupportingTransactions(CTxDB& txdb) +void CWalletTx::AddSupportingTransactions() { vtxPrev.clear(); @@ -694,7 +695,6 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb) BOOST_FOREACH(const CTxIn& txin, vin) vWorkQueue.push_back(txin.prevout.hash); - // This critsect is OK because txdb is already open { LOCK(pwallet->cs_wallet); map<uint256, const CMerkleTx*> mapWalletPrev; @@ -718,15 +718,6 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb) { tx = *mapWalletPrev[hash]; } - else if (!fClient && txdb.ReadDiskTx(hash, tx)) - { - ; - } - else - { - printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); - continue; - } int nDepth = tx.SetMerkleBranch(); vtxPrev.push_back(tx); @@ -764,7 +755,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) block.ReadFromDisk(pindex, true); BOOST_FOREACH(CTransaction& tx, block.vtx) { - if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) + if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) ret++; } pindex = pindex->pnext; @@ -773,49 +764,35 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) return ret; } -int CWallet::ScanForWalletTransaction(const uint256& hashTx) -{ - CTransaction tx; - tx.ReadFromDisk(COutPoint(hashTx, 0)); - if (AddToWalletIfInvolvingMe(tx, NULL, true, true)) - return 1; - return 0; -} - void CWallet::ReacceptWalletTransactions() { - CTxDB txdb("r"); bool fRepeat = true; while (fRepeat) { LOCK(cs_wallet); fRepeat = false; - vector<CDiskTxPos> vMissingTx; + bool fMissing = false; BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) { CWalletTx& wtx = item.second; if (wtx.IsCoinBase() && wtx.IsSpent(0)) continue; - CTxIndex txindex; + CCoins coins; bool fUpdated = false; - if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) + bool fNotFound = pcoinsTip->GetCoins(wtx.GetHash(), coins); + if (!fNotFound || wtx.GetDepthInMainChain() > 0) { // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat - if (txindex.vSpent.size() != wtx.vout.size()) - { - printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %"PRIszu" != wtx.vout.size() %"PRIszu"\n", txindex.vSpent.size(), wtx.vout.size()); - continue; - } - for (unsigned int i = 0; i < txindex.vSpent.size(); i++) + for (unsigned int i = 0; i < wtx.vout.size(); i++) { if (wtx.IsSpent(i)) continue; - if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i])) + if ((i >= coins.vout.size() || coins.vout[i].IsNull()) && IsMine(wtx.vout[i])) { wtx.MarkSpent(i); fUpdated = true; - vMissingTx.push_back(txindex.vSpent[i]); + fMissing = true; } } if (fUpdated) @@ -829,10 +806,10 @@ void CWallet::ReacceptWalletTransactions() { // Re-accept any txes of ours that aren't already in a block if (!wtx.IsCoinBase()) - wtx.AcceptWalletTransaction(txdb, false); + wtx.AcceptWalletTransaction(false); } } - if (!vMissingTx.empty()) + if (fMissing) { // TODO: optimize this to scan just part of the block chain? if (ScanForWalletTransactions(pindexGenesisBlock)) @@ -841,21 +818,22 @@ void CWallet::ReacceptWalletTransactions() } } -void CWalletTx::RelayWalletTransaction(CTxDB& txdb) +void CWalletTx::RelayWalletTransaction() { + CCoinsViewCache& coins = *pcoinsTip; BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) { if (!tx.IsCoinBase()) { uint256 hash = tx.GetHash(); - if (!txdb.ContainsTx(hash)) + if (!coins.HaveCoins(hash)) RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); } } if (!IsCoinBase()) { uint256 hash = GetHash(); - if (!txdb.ContainsTx(hash)) + if (!coins.HaveCoins(hash)) { printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); @@ -863,12 +841,6 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb) } } -void CWalletTx::RelayWalletTransaction() -{ - CTxDB txdb("r"); - RelayWalletTransaction(txdb); -} - void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away @@ -889,7 +861,6 @@ void CWallet::ResendWalletTransactions() // Rebroadcast any of our txes that aren't in a block yet printf("ResendWalletTransactions()\n"); - CTxDB txdb("r"); { LOCK(cs_wallet); // Sort them in chronological order @@ -905,7 +876,7 @@ void CWallet::ResendWalletTransactions() BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) { CWalletTx& wtx = *item.second; - wtx.RelayWalletTransaction(txdb); + wtx.RelayWalletTransaction(); } } } @@ -1160,8 +1131,6 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW { LOCK2(cs_main, cs_wallet); - // txdb must be opened before the mapWallet lock - CTxDB txdb("r"); { nFeeRet = nTransactionFee; loop @@ -1251,7 +1220,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW } // Fill vtxPrev by copying from previous transactions vtxPrev - wtxNew.AddSupportingTransactions(txdb); + wtxNew.AddSupportingTransactions(); wtxNew.fTimeReceivedIsTxTime = true; break; @@ -1371,12 +1340,12 @@ string CWallet::SendMoneyToDestination(const CTxDestination& address, int64 nVal -int CWallet::LoadWallet(bool& fFirstRunRet) +DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { if (!fFileBacked) return DB_LOAD_OK; fFirstRunRet = false; - int nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); + DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); if (nLoadWalletRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) diff --git a/src/wallet.h b/src/wallet.h index 22795b75ba..43b695c597 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -16,11 +16,11 @@ #include "script.h" #include "ui_interface.h" #include "util.h" +#include "walletdb.h" class CAccountingEntry; class CWalletTx; class CReserveKey; -class CWalletDB; class COutput; /** (client) version numbers for particular wallet features */ @@ -162,11 +162,10 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn); - bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); + bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); bool EraseFromWallet(uint256 hash); void WalletUpdateSpent(const CTransaction& prevout); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); - int ScanForWalletTransaction(const uint256& hashTx); void ReacceptWalletTransactions(); void ResendWalletTransactions(); int64 GetBalance() const; @@ -256,7 +255,7 @@ public: } void SetBestChain(const CBlockLocator& loc); - int LoadWallet(bool& fFirstRunRet); + DBErrors LoadWallet(bool& fFirstRunRet); bool SetAddressBookName(const CTxDestination& address, const std::string& strName); @@ -659,12 +658,8 @@ public: int64 GetTxTime() const; int GetRequestCount() const; - void AddSupportingTransactions(CTxDB& txdb); - - bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); - bool AcceptWalletTransaction(); - - void RelayWalletTransaction(CTxDB& txdb); + void AddSupportingTransactions(); + bool AcceptWalletTransaction(bool fCheckInputs=true); void RelayWalletTransaction(); }; diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 0fac0109c8..e102df9720 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -108,7 +108,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountin } -int +DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) { LOCK(pwallet->cs_wallet); @@ -181,16 +181,221 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) } -int CWalletDB::LoadWallet(CWallet* pwallet) +bool +ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, + int& nFileVersion, vector<uint256>& vWalletUpgrade, + bool& fIsEncrypted, bool& fAnyUnordered, string& strType, string& strErr) +{ + try { + // Unserialize + // Taking advantage of the fact that pair serialization + // is just the two items serialized one after the other + ssKey >> strType; + if (strType == "name") + { + string strAddress; + ssKey >> strAddress; + ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()]; + } + else if (strType == "tx") + { + uint256 hash; + ssKey >> hash; + CWalletTx& wtx = pwallet->mapWallet[hash]; + ssValue >> wtx; + if (wtx.CheckTransaction() && (wtx.GetHash() == hash)) + wtx.BindWallet(pwallet); + else + { + pwallet->mapWallet.erase(hash); + return false; + } + + // Undo serialize changes in 31600 + if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) + { + if (!ssValue.empty()) + { + char fTmp; + char fUnused; + ssValue >> fTmp >> fUnused >> wtx.strFromAccount; + strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", + wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = fTmp; + } + else + { + strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = 0; + } + vWalletUpgrade.push_back(hash); + } + + if (wtx.nOrderPos == -1) + fAnyUnordered = true; + + //// debug print + //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); + //printf(" %12"PRI64d" %s %s %s\n", + // wtx.vout[0].nValue, + // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(), + // wtx.hashBlock.ToString().substr(0,20).c_str(), + // wtx.mapValue["message"].c_str()); + } + else if (strType == "acentry") + { + string strAccount; + ssKey >> strAccount; + uint64 nNumber; + ssKey >> nNumber; + if (nNumber > nAccountingEntryNumber) + nAccountingEntryNumber = nNumber; + + if (!fAnyUnordered) + { + CAccountingEntry acentry; + ssValue >> acentry; + if (acentry.nOrderPos == -1) + fAnyUnordered = true; + } + } + else if (strType == "key" || strType == "wkey") + { + vector<unsigned char> vchPubKey; + ssKey >> vchPubKey; + CKey key; + if (strType == "key") + { + CPrivKey pkey; + ssValue >> pkey; + key.SetPubKey(vchPubKey); + if (!key.SetPrivKey(pkey)) + { + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; + } + if (key.GetPubKey() != vchPubKey) + { + strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; + return false; + } + if (!key.IsValid()) + { + strErr = "Error reading wallet database: invalid CPrivKey"; + return false; + } + } + else + { + CWalletKey wkey; + ssValue >> wkey; + key.SetPubKey(vchPubKey); + if (!key.SetPrivKey(wkey.vchPrivKey)) + { + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; + } + if (key.GetPubKey() != vchPubKey) + { + strErr = "Error reading wallet database: CWalletKey pubkey inconsistency"; + return false; + } + if (!key.IsValid()) + { + strErr = "Error reading wallet database: invalid CWalletKey"; + return false; + } + } + if (!pwallet->LoadKey(key)) + { + strErr = "Error reading wallet database: LoadKey failed"; + return false; + } + } + else if (strType == "mkey") + { + unsigned int nID; + ssKey >> nID; + CMasterKey kMasterKey; + ssValue >> kMasterKey; + if(pwallet->mapMasterKeys.count(nID) != 0) + { + strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); + return false; + } + pwallet->mapMasterKeys[nID] = kMasterKey; + if (pwallet->nMasterKeyMaxID < nID) + pwallet->nMasterKeyMaxID = nID; + } + else if (strType == "ckey") + { + vector<unsigned char> vchPubKey; + ssKey >> vchPubKey; + vector<unsigned char> vchPrivKey; + ssValue >> vchPrivKey; + if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) + { + strErr = "Error reading wallet database: LoadCryptedKey failed"; + return false; + } + fIsEncrypted = true; + } + else if (strType == "defaultkey") + { + ssValue >> pwallet->vchDefaultKey; + } + else if (strType == "pool") + { + int64 nIndex; + ssKey >> nIndex; + pwallet->setKeyPool.insert(nIndex); + } + else if (strType == "version") + { + ssValue >> nFileVersion; + if (nFileVersion == 10300) + nFileVersion = 300; + } + else if (strType == "cscript") + { + uint160 hash; + ssKey >> hash; + CScript script; + ssValue >> script; + if (!pwallet->LoadCScript(script)) + { + strErr = "Error reading wallet database: LoadCScript failed"; + return false; + } + } + else if (strType == "orderposnext") + { + ssValue >> pwallet->nOrderPosNext; + } + } catch (...) + { + return false; + } + return true; +} + +static bool IsKeyType(string strType) +{ + return (strType== "key" || strType == "wkey" || + strType == "mkey" || strType == "ckey"); +} + +DBErrors CWalletDB::LoadWallet(CWallet* pwallet) { pwallet->vchDefaultKey = CPubKey(); int nFileVersion = 0; vector<uint256> vWalletUpgrade; bool fIsEncrypted = false; bool fAnyUnordered = false; + bool fNoncriticalErrors = false; + DBErrors result = DB_LOAD_OK; - //// todo: shouldn't we catch exceptions and try to recover and continue? - { + try { LOCK(pwallet->cs_wallet); int nMinVersion = 0; if (Read((string)"minversion", nMinVersion)) @@ -222,189 +427,46 @@ int CWalletDB::LoadWallet(CWallet* pwallet) return DB_CORRUPT; } - // Unserialize - // Taking advantage of the fact that pair serialization - // is just the two items serialized one after the other - string strType; - ssKey >> strType; - if (strType == "name") - { - string strAddress; - ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()]; - } - else if (strType == "tx") - { - uint256 hash; - ssKey >> hash; - CWalletTx& wtx = pwallet->mapWallet[hash]; - ssValue >> wtx; - wtx.BindWallet(pwallet); - - if (wtx.GetHash() != hash) - printf("Error in wallet.dat, hash mismatch\n"); - - // Undo serialize changes in 31600 - if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) - { - if (!ssValue.empty()) - { - char fTmp; - char fUnused; - ssValue >> fTmp >> fUnused >> wtx.strFromAccount; - printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = fTmp; - } - else - { - printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = 0; - } - vWalletUpgrade.push_back(hash); - } - - if (wtx.nOrderPos == -1) - fAnyUnordered = true; - - //// debug print - //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); - //printf(" %12"PRI64d" %s %s %s\n", - // wtx.vout[0].nValue, - // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(), - // wtx.hashBlock.ToString().substr(0,20).c_str(), - // wtx.mapValue["message"].c_str()); - } - else if (strType == "acentry") + // Try to be tolerant of single corrupt records: + string strType, strErr; + if (!ReadKeyValue(pwallet, ssKey, ssValue, nFileVersion, + vWalletUpgrade, fIsEncrypted, fAnyUnordered, strType, strErr)) { - string strAccount; - ssKey >> strAccount; - uint64 nNumber; - ssKey >> nNumber; - if (nNumber > nAccountingEntryNumber) - nAccountingEntryNumber = nNumber; - - if (!fAnyUnordered) - { - CAccountingEntry acentry; - ssValue >> acentry; - if (acentry.nOrderPos == -1) - fAnyUnordered = true; - } - } - else if (strType == "key" || strType == "wkey") - { - vector<unsigned char> vchPubKey; - ssKey >> vchPubKey; - CKey key; - if (strType == "key") - { - CPrivKey pkey; - ssValue >> pkey; - key.SetPubKey(vchPubKey); - key.SetPrivKey(pkey); - if (key.GetPubKey() != vchPubKey) - { - printf("Error reading wallet database: CPrivKey pubkey inconsistency\n"); - return DB_CORRUPT; - } - if (!key.IsValid()) - { - printf("Error reading wallet database: invalid CPrivKey\n"); - return DB_CORRUPT; - } - } + // losing keys is considered a catastrophic error, anything else + // we assume the user can live with: + if (IsKeyType(strType)) + result = DB_CORRUPT; else { - CWalletKey wkey; - ssValue >> wkey; - key.SetPubKey(vchPubKey); - key.SetPrivKey(wkey.vchPrivKey); - if (key.GetPubKey() != vchPubKey) - { - printf("Error reading wallet database: CWalletKey pubkey inconsistency\n"); - return DB_CORRUPT; - } - if (!key.IsValid()) - { - printf("Error reading wallet database: invalid CWalletKey\n"); - return DB_CORRUPT; - } - } - if (!pwallet->LoadKey(key)) - { - printf("Error reading wallet database: LoadKey failed\n"); - return DB_CORRUPT; - } - } - else if (strType == "mkey") - { - unsigned int nID; - ssKey >> nID; - CMasterKey kMasterKey; - ssValue >> kMasterKey; - if(pwallet->mapMasterKeys.count(nID) != 0) - { - printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID); - return DB_CORRUPT; - } - pwallet->mapMasterKeys[nID] = kMasterKey; - if (pwallet->nMasterKeyMaxID < nID) - pwallet->nMasterKeyMaxID = nID; - } - else if (strType == "ckey") - { - vector<unsigned char> vchPubKey; - ssKey >> vchPubKey; - vector<unsigned char> vchPrivKey; - ssValue >> vchPrivKey; - if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) - { - printf("Error reading wallet database: LoadCryptedKey failed\n"); - return DB_CORRUPT; + // Leave other errors alone, if we try to fix them we might make things worse. + fNoncriticalErrors = true; // ... but do warn the user there is something wrong. + if (strType == "tx") + // Rescan if there is a bad transaction record: + SoftSetBoolArg("-rescan", true); } - fIsEncrypted = true; - } - else if (strType == "defaultkey") - { - ssValue >> pwallet->vchDefaultKey; - } - else if (strType == "pool") - { - int64 nIndex; - ssKey >> nIndex; - pwallet->setKeyPool.insert(nIndex); - } - else if (strType == "version") - { - ssValue >> nFileVersion; - if (nFileVersion == 10300) - nFileVersion = 300; - } - else if (strType == "cscript") - { - uint160 hash; - ssKey >> hash; - CScript script; - ssValue >> script; - if (!pwallet->LoadCScript(script)) - { - printf("Error reading wallet database: LoadCScript failed\n"); - return DB_CORRUPT; - } - } - else if (strType == "orderposnext") - { - ssValue >> pwallet->nOrderPosNext; } + if (!strErr.empty()) + printf("%s\n", strErr.c_str()); } pcursor->close(); } + catch (...) + { + result = DB_CORRUPT; + } - BOOST_FOREACH(uint256 hash, vWalletUpgrade) - WriteTx(hash, pwallet->mapWallet[hash]); + if (fNoncriticalErrors && result == DB_LOAD_OK) + result = DB_NONCRITICAL_ERROR; + + // Any wallet corruption at all: skip any rewriting or + // upgrading, we don't want to make it worse. + if (result != DB_LOAD_OK) + return result; printf("nFileVersion = %d\n", nFileVersion); + BOOST_FOREACH(uint256 hash, vWalletUpgrade) + WriteTx(hash, pwallet->mapWallet[hash]); // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000)) @@ -414,10 +476,9 @@ int CWalletDB::LoadWallet(CWallet* pwallet) WriteVersion(CLIENT_VERSION); if (fAnyUnordered) - return ReorderTransactions(pwallet); + result = ReorderTransactions(pwallet); - // If you add anything else here... be sure to do it if ReorderTransactions returns DB_LOAD_OK too! - return DB_LOAD_OK; + return result; } void ThreadFlushWalletDB(void* parg) @@ -521,3 +582,94 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) } return false; } + +// +// Try to (very carefully!) recover wallet.dat if there is a problem. +// +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) +{ + // Recovery procedure: + // move wallet.dat to wallet.timestamp.bak + // Call Salvage with fAggressive=true to + // get as much data as possible. + // Rewrite salvaged data to wallet.dat + // Set -rescan so any missing transactions will be + // found. + int64 now = GetTime(); + std::string newFilename = strprintf("wallet.%"PRI64d".bak", now); + + int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL, + newFilename.c_str(), DB_AUTO_COMMIT); + if (result == 0) + printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str()); + else + { + printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str()); + return false; + } + + std::vector<CDBEnv::KeyValPair> salvagedData; + bool allOK = dbenv.Salvage(newFilename, true, salvagedData); + if (salvagedData.empty()) + { + printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str()); + return false; + } + printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size()); + + bool fSuccess = allOK; + Db* pdbCopy = new Db(&dbenv.dbenv, 0); + int ret = pdbCopy->open(NULL, // Txn pointer + filename.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) + { + printf("Cannot create database file %s\n", filename.c_str()); + return false; + } + CWallet dummyWallet; + int nFileVersion = 0; + vector<uint256> vWalletUpgrade; + bool fIsEncrypted = false; + bool fAnyUnordered = false; + + DbTxn* ptxn = dbenv.TxnBegin(); + BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData) + { + if (fOnlyKeys) + { + CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); + CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); + string strType, strErr; + bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, + nFileVersion, vWalletUpgrade, + fIsEncrypted, fAnyUnordered, + strType, strErr); + if (!IsKeyType(strType)) + continue; + if (!fReadOK) + { + printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str()); + continue; + } + } + Dbt datKey(&row.first[0], row.first.size()); + Dbt datValue(&row.second[0], row.second.size()); + int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + ptxn->commit(0); + pdbCopy->close(0); + delete pdbCopy; + + return fSuccess; +} + +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) +{ + return CWalletDB::Recover(dbenv, filename, false); +} diff --git a/src/walletdb.h b/src/walletdb.h index f078481811..a3e779ab9d 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -17,6 +17,7 @@ enum DBErrors { DB_LOAD_OK, DB_CORRUPT, + DB_NONCRITICAL_ERROR, DB_TOO_NEW, DB_LOAD_FAIL, DB_NEED_REWRITE @@ -153,8 +154,10 @@ public: int64 GetAccountCreditDebit(const std::string& strAccount); void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries); - int ReorderTransactions(CWallet*); - int LoadWallet(CWallet* pwallet); + DBErrors ReorderTransactions(CWallet*); + DBErrors LoadWallet(CWallet* pwallet); + static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); + static bool Recover(CDBEnv& dbenv, std::string filename); }; #endif // BITCOIN_WALLETDB_H |