summaryrefslogtreecommitdiff
path: root/src/utils/cpp/incremental_reader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/cpp/incremental_reader.cpp')
-rw-r--r--src/utils/cpp/incremental_reader.cpp139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/utils/cpp/incremental_reader.cpp b/src/utils/cpp/incremental_reader.cpp
new file mode 100644
index 00000000..c1b5bc3a
--- /dev/null
+++ b/src/utils/cpp/incremental_reader.cpp
@@ -0,0 +1,139 @@
+// 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 <cstdint>
+#include <exception>
+#include <string_view>
+
+#include "fmt/core.h"
+#include "src/utils/cpp/in_place_visitor.hpp"
+
+namespace {
+void DisposeFile(gsl::owner<std::FILE*> 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<IncrementalReader, std::string> {
+ if (chunk_size == 0) {
+ return unexpected<std::string>{
+ "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::FILE>{
+ 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<std::string_view, std::string> {
+ using Result = expected<std::string_view, std::string>;
+ 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<std::string>{
+ "IncrementalReader: ReadChunk got an unknown exception"};
+ }
+}
+
+auto IncrementalReader::ReadFromFile(FileSource const& file, std::size_t offset)
+ const -> expected<std::string_view, std::string> {
+ if (file == nullptr) {
+ return unexpected<std::string>{
+ "IncrementalReader: ReadFromFile: got corrupted file"};
+ }
+
+ if (std::fseek(file.get(), gsl::narrow<std::int64_t>(offset), SEEK_SET) !=
+ 0) {
+ return unexpected<std::string>{
+ "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<IncrementalReader const*> const& owner,
+ std::size_t offset) noexcept
+ : owner_{owner}, offset_{offset} {}
+
+auto IncrementalReader::Iterator::operator*() const noexcept
+ -> expected<std::string_view, std::string> {
+ 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;
+}