// Copyright 2025 Huawei Cloud Computing Technology Co., Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "src/utils/cpp/incremental_reader.hpp" #include #include #include #include "fmt/core.h" #include "src/utils/cpp/in_place_visitor.hpp" namespace { void DisposeFile(gsl::owner file) noexcept { if (file == nullptr) { return; } std::fclose(file); } } // namespace auto IncrementalReader::FromFile(std::size_t chunk_size, std::filesystem::path const& path) noexcept -> expected { if (chunk_size == 0) { return unexpected{ "IncrementalReader: the chunk size cannot be 0"}; } try { // Ensure this is a file: if (not std::filesystem::is_regular_file(path)) { return unexpected{fmt::format( "IncrementalReader: not a file :\n {} ", path.string())}; } // Open file for reading: static constexpr std::string_view kReadBinary = "rb"; auto file = std::shared_ptr{ std::fopen(path.c_str(), kReadBinary.data()), ::DisposeFile}; if (file == nullptr) { return unexpected{ fmt::format("IncrementalReader: failed to open the file:\n{}", path.string())}; } std::size_t const content_size = std::filesystem::file_size(path); return IncrementalReader{chunk_size, content_size, std::move(file), /*buffer=*/std::string(chunk_size, '\0')}; } catch (std::exception const& e) { return unexpected{fmt::format( "IncrementalReader: While processing {}\ngot an exception: {}", path.string(), e.what())}; } catch (...) { return unexpected{fmt::format( "IncrementalReader: While processing {}\ngot an unknown exception", path.string())}; } } auto IncrementalReader::ReadChunk(std::size_t offset) const noexcept -> expected { using Result = expected; InPlaceVisitor const visitor{ [this, offset](FileSource const& file) -> Result { return ReadFromFile(file, offset); }, }; try { return std::visit(visitor, content_); } catch (std::exception const& e) { return unexpected{fmt::format( "IncrementalReader: ReadChunk got an exception:\n{}", e.what())}; } catch (...) { return unexpected{ "IncrementalReader: ReadChunk got an unknown exception"}; } } auto IncrementalReader::ReadFromFile(FileSource const& file, std::size_t offset) const -> expected { if (file == nullptr) { return unexpected{ "IncrementalReader: ReadFromFile: got corrupted file"}; } if (std::fseek(file.get(), gsl::narrow(offset), SEEK_SET) != 0) { return unexpected{ "IncrementalReader: ReadFromFile: failed to set offset"}; } std::size_t read = 0; while (std::feof(file.get()) == 0 and std::ferror(file.get()) == 0 and read < buffer_.size()) { read += std::fread( &buffer_[read], sizeof(char), buffer_.size() - read, file.get()); } if (std::ferror(file.get()) != 0) { return unexpected{ fmt::format("IncrementalReader: ReadFromFile: ferror {}", std::ferror(file.get()))}; } return std::string_view{buffer_.data(), read}; } IncrementalReader::Iterator::Iterator( gsl::not_null const& owner, std::size_t offset) noexcept : owner_{owner}, offset_{offset} {} auto IncrementalReader::Iterator::operator*() const noexcept -> expected { return owner_->ReadChunk(offset_); } auto IncrementalReader::Iterator::operator++() noexcept -> IncrementalReader::Iterator& { offset_ += owner_->chunk_size_; if (offset_ >= owner_->content_size_) { offset_ = owner_->GetEndOffset(); } return *this; }