/* vim:set ts=2 sw=2 sts=2 et: */ /** * \author Marcus Holland-Moritz (github@mhxnet.de) * \copyright Copyright (c) Marcus Holland-Moritz * * This file is part of dwarfs. * * dwarfs is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * dwarfs is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with dwarfs. If not, see . */ #include #ifndef _WIN32 #include #endif #include #include #include #include #include #include namespace dwarfs::reader::internal { using namespace dwarfs::internal; namespace { template class cached_block_ final : public cached_block { public: cached_block_(logger& lgr, fs_section const& b, std::shared_ptr mm, bool release, bool disable_integrity_check) : decompressor_(std::make_unique( b.compression(), mm->as(b.start()), b.length(), data_)) , mm_(std::move(mm)) , section_(b) , LOG_PROXY_INIT(lgr) , release_(release) , uncompressed_size_{decompressor_->uncompressed_size()} { if (!disable_integrity_check && !section_.check(*mm_)) { DWARFS_THROW(runtime_error, "block data integrity check failed"); } } ~cached_block_() override { if (decompressor_) { try_release(); } } // once the block is fully decompressed, we can reset the decompressor_ // This can be called from any thread size_t range_end() const override { return range_end_.load(); } const uint8_t* data() const override { return data_.data(); } void decompress_until(size_t end) override { while (data_.size() < end) { if (!decompressor_) { DWARFS_THROW(runtime_error, "no decompressor for block"); } if (decompressor_->decompress_frame()) { // We're done, free the memory decompressor_.reset(); // And release the memory from the mapping try_release(); } range_end_ = data_.size(); } } size_t uncompressed_size() const override { return uncompressed_size_; } void touch() override { last_access_ = std::chrono::steady_clock::now(); } bool last_used_before(std::chrono::steady_clock::time_point tp) const override { return last_access_ < tp; } bool any_pages_swapped_out(std::vector& tmp [[maybe_unused]]) const override { #if !(defined(_WIN32) || defined(__APPLE__)) // TODO: should be possible to do this on Windows and macOS as well auto page_size = ::sysconf(_SC_PAGESIZE); tmp.resize((data_.size() + page_size - 1) / page_size); if (::mincore(const_cast(data_.data()), data_.size(), tmp.data()) == 0) { // i&1 == 1 means resident in memory return std::any_of(tmp.begin(), tmp.end(), [](auto i) { return (i & 1) == 0; }); } #endif return false; } private: void try_release() { if (release_) { if (auto ec = mm_->release(section_.start(), section_.length())) { LOG_INFO << "madvise() failed: " << ec.message(); } } } std::atomic range_end_{0}; std::vector data_; std::unique_ptr decompressor_; std::shared_ptr mm_; fs_section section_; LOG_PROXY_DECL(LoggerPolicy); bool const release_; size_t const uncompressed_size_; std::chrono::steady_clock::time_point last_access_; }; } // namespace std::unique_ptr cached_block::create(logger& lgr, fs_section const& b, std::shared_ptr mm, bool release, bool disable_integrity_check) { return make_unique_logging_object( lgr, b, std::move(mm), release, disable_integrity_check); } } // namespace dwarfs::reader::internal