blob: f9283975b678293b5929eca2393a8e7614211a40 [file] [log] [blame]
// Copyright 2020 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 "hex_dump_generator.h"
#include <lib/cksum.h>
#include <lib/zx/status.h>
#include <sys/stat.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <cstdint>
#include <iomanip>
#include <memory>
#include <string>
#include <fbl/unique_fd.h>
namespace extractor {
zx::status<std::unique_ptr<HexDumpGenerator>> HexDumpGenerator::Create(
fbl::unique_fd input, HexDumpGeneratorOptions options) {
if (!input || options.bytes_per_line == 0) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (lseek(input.get(), 0, SEEK_SET) != 0) {
return zx::error(ZX_ERR_IO);
}
struct stat st;
if (fstat(input.get(), &st) != 0) {
return zx::error(ZX_ERR_FILE_BIG);
}
return zx::ok(std::unique_ptr<HexDumpGenerator>(
new HexDumpGenerator(std::move(input), st.st_size, std::move(options))));
}
std::string HexDumpGenerator::BuildLine(const std::string& hex_string, off_t offset,
size_t size) const {
std::stringstream stream;
if (options_.tag.length() > 0) {
stream << options_.tag << " ";
}
if (options_.dump_offset) {
stream << offset << "-" << offset + size - 1 << ":";
}
stream << hex_string << std::endl;
return stream.str();
}
zx::status<std::string> HexDumpGenerator::DumpToString(const uint8_t* buffer, off_t offset,
size_t size) {
ZX_ASSERT(size > 0 && size <= options_.bytes_per_line);
std::stringstream stream;
std::stringstream current_line;
for (size_t i = 0; i < size; i++) {
current_line << std::setfill('0') << std::setw(2) << std::right << std::hex << +buffer[i];
}
if (last_hex_string_ != current_line.str()) {
if (skipped_bytes_ > 0) {
stream << BuildLine("*", skip_start_offset_, skipped_bytes_);
}
stream << BuildLine(current_line.str(), offset, size);
last_hex_string_ = current_line.str();
skipped_bytes_ = 0;
} else {
if (skipped_bytes_ == 0) {
skip_start_offset_ = offset;
}
skipped_bytes_ += options_.bytes_per_line;
}
if (Done()) {
// If we are done dumping the file then the last line might be a repetation of previous line. If
// so dump the repeated line.
if (skipped_bytes_ > 0) {
stream << BuildLine("*", skip_start_offset_, skipped_bytes_);
}
if (options_.dump_checksum) {
stream << BuildLine("checksum: " + std::to_string(crc32_), 0, file_size_);
}
}
return zx::ok(stream.str());
}
bool HexDumpGenerator::Done() const { return current_offset_ == file_size_; }
zx::status<std::string> HexDumpGenerator::GetNextLine() {
if (Done()) {
return zx::error(ZX_ERR_STOP);
}
size_t bytes_read = 0;
uint8_t buffer[options_.bytes_per_line];
while (bytes_read != options_.bytes_per_line) {
auto ret = read(input_.get(), buffer + bytes_read, options_.bytes_per_line - bytes_read);
if (ret < 0) {
return zx::error(ZX_ERR_IO);
}
if (ret == 0) {
break;
}
bytes_read += static_cast<size_t>(ret);
}
if (bytes_read == 0) {
// We have checked whether we are "Done()" in the beginning. read should not return zero.
return zx::error(ZX_ERR_BAD_STATE);
}
if (options_.dump_checksum) {
crc32_ = crc32(crc32_, buffer, bytes_read);
}
off_t offset = current_offset_;
current_offset_ += static_cast<off_t>(bytes_read);
auto status = DumpToString(buffer, offset, bytes_read);
return status;
}
} // namespace extractor