| 795 | // --------------------------------------------------------------------------- |
| 796 | |
| 797 | void NetworkChunkCache::insert(PJ_CONTEXT *ctx, const std::string &url, |
| 798 | unsigned long long chunkIdx, |
| 799 | std::vector<unsigned char> &&data) { |
| 800 | auto dataPtr(std::make_shared<std::vector<unsigned char>>(std::move(data))); |
| 801 | cache_.insert(Key(url, chunkIdx), dataPtr); |
| 802 | |
| 803 | auto diskCache = DiskChunkCache::open(ctx); |
| 804 | if (!diskCache) |
| 805 | return; |
| 806 | auto hDB = diskCache->handle(); |
| 807 | |
| 808 | // Always insert DOWNLOAD_CHUNK_SIZE bytes to avoid fragmentation |
| 809 | std::vector<unsigned char> blob(*dataPtr); |
| 810 | assert(blob.size() <= DOWNLOAD_CHUNK_SIZE); |
| 811 | blob.resize(DOWNLOAD_CHUNK_SIZE); |
| 812 | |
| 813 | // Check if there is an existing entry for that URL and offset |
| 814 | auto stmt = diskCache->prepare( |
| 815 | "SELECT id, data_id FROM chunks WHERE url = ? AND offset = ?"); |
| 816 | if (!stmt) |
| 817 | return; |
| 818 | stmt->bindText(url.c_str()); |
| 819 | stmt->bindInt64(chunkIdx * DOWNLOAD_CHUNK_SIZE); |
| 820 | |
| 821 | const auto mainRet = stmt->execute(); |
| 822 | if (mainRet == SQLITE_ROW) { |
| 823 | const auto chunk_id = stmt->getInt64(); |
| 824 | const auto data_id = stmt->getInt64(); |
| 825 | stmt = |
| 826 | diskCache->prepare("UPDATE chunk_data SET data = ? WHERE id = ?"); |
| 827 | if (!stmt) |
| 828 | return; |
| 829 | stmt->bindBlob(blob.data(), blob.size()); |
| 830 | stmt->bindInt64(data_id); |
| 831 | { |
| 832 | const auto ret = stmt->execute(); |
| 833 | if (ret != SQLITE_DONE) { |
| 834 | pj_log(ctx, PJ_LOG_ERROR, "%s", sqlite3_errmsg(hDB)); |
| 835 | return; |
| 836 | } |
| 837 | } |
| 838 | |
| 839 | diskCache->move_to_head(chunk_id); |
| 840 | |
| 841 | return; |
| 842 | } else if (mainRet != SQLITE_DONE) { |
| 843 | pj_log(ctx, PJ_LOG_ERROR, "%s", sqlite3_errmsg(hDB)); |
| 844 | return; |
| 845 | } |
| 846 | |
| 847 | // Lambda to recycle an existing entry that was either invalidated, or |
| 848 | // least recently used. |
| 849 | const auto reuseExistingEntry = |
| 850 | [ctx, &blob, &diskCache, hDB, &url, chunkIdx, |
| 851 | &dataPtr](std::unique_ptr<SQLiteStatement> &stmtIn) { |
| 852 | const auto chunk_id = stmtIn->getInt64(); |
| 853 | const auto data_id = stmtIn->getInt64(); |
| 854 | if (data_id <= 0) { |
no test coverage detected