blob: 48d23a0d9473c2bea6bf75818600826b77e1f576 [file] [log] [blame]
// 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