| 773 | // --------------------------------------------------------------------------- |
| 774 | |
| 775 | void NetworkChunkCache::insert(PJ_CONTEXT *ctx, const std::string &url, |
| 776 | unsigned long long chunkIdx, |
| 777 | std::vector<unsigned char> &&data) { |
| 778 | auto dataPtr(std::make_shared<std::vector<unsigned char>>(std::move(data))); |
| 779 | cache_.insert(Key(url, chunkIdx), dataPtr); |
| 780 | |
| 781 | auto diskCache = DiskChunkCache::open(ctx); |
| 782 | if (!diskCache) |
| 783 | return; |
| 784 | auto hDB = diskCache->handle(); |
| 785 | |
| 786 | // Always insert DOWNLOAD_CHUNK_SIZE bytes to avoid fragmentation |
| 787 | std::vector<unsigned char> blob(*dataPtr); |
| 788 | assert(blob.size() <= DOWNLOAD_CHUNK_SIZE); |
| 789 | blob.resize(DOWNLOAD_CHUNK_SIZE); |
| 790 | |
| 791 | // Check if there is an existing entry for that URL and offset |
| 792 | auto stmt = diskCache->prepare( |
| 793 | "SELECT id, data_id FROM chunks WHERE url = ? AND offset = ?"); |
| 794 | if (!stmt) |
| 795 | return; |
| 796 | stmt->bindText(url.c_str()); |
| 797 | stmt->bindInt64(chunkIdx * DOWNLOAD_CHUNK_SIZE); |
| 798 | |
| 799 | const auto mainRet = stmt->execute(); |
| 800 | if (mainRet == SQLITE_ROW) { |
| 801 | const auto chunk_id = stmt->getInt64(); |
| 802 | const auto data_id = stmt->getInt64(); |
| 803 | stmt = |
| 804 | diskCache->prepare("UPDATE chunk_data SET data = ? WHERE id = ?"); |
| 805 | if (!stmt) |
| 806 | return; |
| 807 | stmt->bindBlob(blob.data(), blob.size()); |
| 808 | stmt->bindInt64(data_id); |
| 809 | { |
| 810 | const auto ret = stmt->execute(); |
| 811 | if (ret != SQLITE_DONE) { |
| 812 | pj_log(ctx, PJ_LOG_ERROR, "%s", sqlite3_errmsg(hDB)); |
| 813 | return; |
| 814 | } |
| 815 | } |
| 816 | |
| 817 | diskCache->move_to_head(chunk_id); |
| 818 | |
| 819 | return; |
| 820 | } else if (mainRet != SQLITE_DONE) { |
| 821 | pj_log(ctx, PJ_LOG_ERROR, "%s", sqlite3_errmsg(hDB)); |
| 822 | return; |
| 823 | } |
| 824 | |
| 825 | // Lambda to recycle an existing entry that was either invalidated, or |
| 826 | // least recently used. |
| 827 | const auto reuseExistingEntry = |
| 828 | [ctx, &blob, &diskCache, hDB, &url, chunkIdx, |
| 829 | &dataPtr](std::unique_ptr<SQLiteStatement> &stmtIn) { |
| 830 | const auto chunk_id = stmtIn->getInt64(); |
| 831 | const auto data_id = stmtIn->getInt64(); |
| 832 | if (data_id <= 0) { |
no test coverage detected