| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "dump-file-stdio.h" |
| |
| #include "buffer-impl.h" |
| |
| namespace zxdump::internal { |
| |
| DumpFile::Stdio::~Stdio() = default; |
| |
| // Return the available subset of the requested data, a view valid for the |
| // life of the Stdio. |
| fit::result<Error, ByteView> DumpFile::Stdio::ReadPermanent(FileRange where) { |
| auto result = Read(where); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| if (result->size() < where.size) { |
| return TruncatedDump(); |
| } |
| ByteView data{result->data(), result->size()}; |
| keepalive_.push_front(std::move(result).value()); |
| return fit::ok(data); |
| } |
| |
| // Return the available subset of the requested data, a view valid only |
| // until the next call to this method. The returned data might be less |
| // than what's requested if EOF is reached. |
| fit::result<Error, ByteView> DumpFile::Stdio::ReadProbe(FileRange where) { |
| auto result = Read(where); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| ByteView data{result->data(), result->size()}; |
| ephemeral_buffer_ = std::move(result).value(); |
| ephemeral_buffer_range_ = where; |
| return fit::ok(data); |
| } |
| |
| // Return the available subset of the requested data, a view valid only |
| // until the next call to this method. The data must be present. |
| fit::result<Error, ByteView> DumpFile::Stdio::ReadEphemeral(FileRange where) { |
| auto result = ReadProbe(where); |
| if (result.is_ok() && result->size() < where.size) { |
| return TruncatedDump(); |
| } |
| return result; |
| } |
| |
| fit::result<Error, Buffer<>> DumpFile::Stdio::ReadMemory(FileRange where) { |
| auto result = Read(where); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| Buffer<> buffer; |
| auto copy = std::make_unique<BufferImplVector>(*std::move(result)); |
| buffer.data_ = cpp20::span<std::byte>(*copy); |
| buffer.impl_ = std::move(copy); |
| return fit::ok(std::move(buffer)); |
| } |
| |
| fit::result<Error, DumpFile::ByteVector> DumpFile::Stdio::Read(FileRange where) { |
| if (!stream_) { |
| return fit::error(Error{"read_memory disabled", ZX_ERR_NOT_SUPPORTED}); |
| } |
| |
| // Seek if necessary and possible. |
| if (where.offset != pos_ && !is_pipe_) { |
| if (fseek(stream_.get(), static_cast<long int>(where.offset), SEEK_SET) == 0) { |
| pos_ = where.offset; |
| } else if (errno == ESPIPE) { |
| is_pipe_ = true; |
| } else { |
| return fit::error(Error{"fseek", ZX_ERR_IO}); |
| } |
| } |
| |
| // In general the reader can only ever need to look backward when attempting |
| // random access for reading memory segments. The one exception is after |
| // reading the initial header probe with ReadEphemeral, when the next data |
| // needed might overlap with the end of the probe that was more than it |
| // turned out was actually needed for the header. So in that case we can |
| // steal the data from the ephemeral_buffer_ already on hand. |
| ByteVector buffer(where.size); |
| std::byte* data = buffer.data(); |
| while (where.offset < pos_) { |
| ByteView old_data{ephemeral_buffer_.data(), ephemeral_buffer_range_.size}; |
| if (where.offset >= ephemeral_buffer_range_.offset) { |
| size_t ofs = where.offset - ephemeral_buffer_range_.offset; |
| if (ofs < old_data.size()) { |
| size_t copied = std::min(old_data.size() - ofs, static_cast<size_t>(where.size)); |
| std::copy(old_data.begin() + ofs, old_data.begin() + ofs + copied, data); |
| data += copied; |
| where.offset += copied; |
| where.size -= copied; |
| continue; |
| } |
| } |
| return fit::error(Error{"random access not available", ZX_ERR_IO_REFUSED}); |
| } |
| |
| // Not seekable, so just eat any data being skipped over. |
| while (where.offset > pos_) { |
| if (getc(stream_.get()) == EOF) { |
| return fit::error(Error{"getc", ZX_ERR_IO}); |
| } |
| ++pos_; |
| } |
| |
| while (where.size > 0) { |
| size_t n = fread(data, 1, where.size, stream_.get()); |
| if (n == 0) { |
| if (feof(stream_.get())) { |
| break; |
| } |
| return fit::error(Error{"fread", ZX_ERR_IO}); |
| } |
| pos_ += n; |
| data += n; |
| where.size -= n; |
| } |
| buffer.resize(data - buffer.data()); |
| buffer.shrink_to_fit(); |
| return fit::ok(std::move(buffer)); |
| } |
| |
| } // namespace zxdump::internal |