aboutsummaryrefslogtreecommitdiff
path: root/disassembler_ztf.cc
diff options
context:
space:
mode:
Diffstat (limited to 'disassembler_ztf.cc')
-rw-r--r--disassembler_ztf.cc647
1 files changed, 647 insertions, 0 deletions
diff --git a/disassembler_ztf.cc b/disassembler_ztf.cc
new file mode 100644
index 0000000..f938d42
--- /dev/null
+++ b/disassembler_ztf.cc
@@ -0,0 +1,647 @@
+// Copyright 2018 The Chromium 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 "components/zucchini/disassembler_ztf.h"
+
+#include <algorithm>
+#include <cmath>
+#include <iterator>
+#include <numeric>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/checked_math.h"
+#include "components/zucchini/algorithm.h"
+#include "components/zucchini/buffer_source.h"
+#include "components/zucchini/buffer_view.h"
+#include "components/zucchini/io_utils.h"
+
+namespace zucchini {
+
+namespace {
+
+constexpr uint8_t kDelimiter = ',';
+
+constexpr int kHeaderMagicSize = 4;
+constexpr int kFooterMagicSize = 5;
+constexpr int kTotalMagicSize = kHeaderMagicSize + kFooterMagicSize;
+
+// Number of characters that aren't digits in each type of reference.
+constexpr int kNumConstCharInAbs = 3;
+constexpr int kNumConstCharInRel = 5;
+
+/******** ZtfConfig ********/
+
+// For passing around metadata about the type of reference to match.
+// - |digits_per_dim| is the length of the offset in lines/cols of a
+// reference.
+// - |open_char| is an ASCII character representing the opening char.
+// - |close_char| is an ASCII character representing the closing char.
+struct ZtfConfig {
+ uint8_t digits_per_dim;
+ uint8_t open_char;
+ uint8_t close_char;
+
+ constexpr uint8_t abs_width() const {
+ return digits_per_dim * 2 + kNumConstCharInAbs;
+ }
+
+ constexpr uint8_t rel_width() const {
+ return digits_per_dim * 2 + kNumConstCharInRel;
+ }
+
+ uint8_t Width(ztf::LineCol /* lc */) const { return abs_width(); }
+
+ uint8_t Width(ztf::DeltaLineCol /* dlc */) const { return rel_width(); }
+};
+
+// Creates a ZtfConfig for parsing or writing based on the desired |digits| and
+// |pool|.
+template <DisassemblerZtf::ReferencePool pool>
+constexpr ZtfConfig MakeZtfConfig(uint8_t digits) {
+ switch (pool) {
+ case DisassemblerZtf::kAngles:
+ return ZtfConfig{digits, '<', '>'};
+ case DisassemblerZtf::kBraces:
+ return ZtfConfig{digits, '{', '}'};
+ case DisassemblerZtf::kBrackets:
+ return ZtfConfig{digits, '[', ']'};
+ case DisassemblerZtf::kParentheses:
+ break; // Handled below.
+ }
+ return ZtfConfig{digits, '(', ')'};
+}
+
+/******** ZtfParser ********/
+
+// ZtfParser is used to extract (absolute) LineCol and (relative) DeltaLineCol
+// from a ZTF file, and contains various helpers for character, digits, and sign
+// matching.
+class ZtfParser {
+ public:
+ ZtfParser(offset_t hi, ConstBufferView image, ZtfConfig config)
+ : image_(image), hi_(hi), config_(config) {
+ DCHECK_LE(static_cast<size_t>(std::pow(10U, config_.digits_per_dim)),
+ ztf::kMaxDimValue);
+ }
+
+ // Attempts to match an absolute reference at |offset|. If successful then
+ // assigns the result to |abs_lc| and returns true. Otherwise returns false.
+ // An absolute reference takes the form:
+ // <open><digits><delimiter><digits><close>
+ bool MatchAtOffset(offset_t offset, ztf::LineCol* abs_lc) {
+ if (hi_ < config_.abs_width() || offset > hi_ - config_.abs_width())
+ return false;
+ offset_ = offset;
+ return MatchChar(config_.open_char) && MatchDigits(+1, &abs_lc->line) &&
+ MatchChar(kDelimiter) && MatchDigits(+1, &abs_lc->col) &&
+ MatchChar(config_.close_char);
+ }
+
+ // Attempts to match an absolute reference at |offset|. If successful then
+ // assigns the result to |rel_lc| and returns true. Otherwise returns false. A
+ // relative reference takes the form:
+ // <open><sign><digits><delimiter><sign><digits><close>
+ bool MatchAtOffset(offset_t offset, ztf::DeltaLineCol* rel_dlc) {
+ if (hi_ < config_.rel_width() || offset > hi_ - config_.rel_width())
+ return false;
+ offset_ = offset;
+ ztf::dim_t line_sign;
+ ztf::dim_t col_sign;
+ return MatchChar(config_.open_char) && MatchSign(&line_sign) &&
+ MatchDigits(line_sign, &rel_dlc->line) && MatchChar(kDelimiter) &&
+ MatchSign(&col_sign) && MatchDigits(col_sign, &rel_dlc->col) &&
+ MatchChar(config_.close_char);
+ }
+
+ private:
+ // The Match*() functions below can advance |offset_|, and return a bool to
+ // indicate success to allow chaining using &&.
+
+ // Returns true if |character| is at location |offset_| in |image_| and
+ // increments |offset_|.
+ bool MatchChar(uint8_t character) {
+ return character == image_.read<uint8_t>(offset_++);
+ }
+
+ // Looks for '+' or '-' at |offset_|. If found, stores +1 or -1 in |sign| and
+ // returns true. Otherwise returns false.
+ bool MatchSign(ztf::dim_t* sign) {
+ uint8_t val = image_.read<uint8_t>(offset_++);
+ if (val == static_cast<uint8_t>(ztf::SignChar::kMinus)) {
+ *sign = -1;
+ return true;
+ }
+ if (val == static_cast<uint8_t>(ztf::SignChar::kPlus)) {
+ *sign = 1;
+ return true;
+ }
+ return false;
+ }
+
+ // Attempts to extract a number with the number of base 10 digits equal to
+ // |config_.digits_per_dim| from |image_| starting from |offset_|. Returns
+ // true and assigns the integer value to |value| if successful.
+ bool MatchDigits(ztf::dim_t sign, ztf::dim_t* value) {
+ ztf::dim_t output = 0;
+ for (int i = 0; i < config_.digits_per_dim; ++i) {
+ auto digit = image_.read<uint8_t>(offset_++);
+ if (digit >= '0' && digit < '0' + 10)
+ output = output * 10 + digit - '0';
+ else
+ return false;
+ }
+ if (!output && sign < 0) // Disallow "-0", "-00", etc.
+ return false;
+ *value = sign * output;
+ return true;
+ }
+
+ ConstBufferView image_;
+ const offset_t hi_;
+ const ZtfConfig config_;
+ offset_t offset_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(ZtfParser);
+};
+
+/******** ZtfWriter ********/
+
+// ZtfWriter is used to write references to an image. This includes writing
+// the enclosing characters around the reference.
+class ZtfWriter {
+ public:
+ ZtfWriter(MutableBufferView image, ZtfConfig config)
+ : image_(image),
+ config_(config),
+ val_bound_(
+ static_cast<ztf::dim_t>(std::pow(10, config_.digits_per_dim))) {}
+
+ // Write an absolute reference |abs_ref| at |offset|. Note that references
+ // that would overwrite a newline are skipped as this would invalidate all
+ // the other reference line numbers.
+ void Write(offset_t offset, ztf::LineCol abs_ref) {
+ offset_ = offset;
+ if (!SafeToWriteNumber(abs_ref.line) || !SafeToWriteNumber(abs_ref.col) ||
+ !SafeToWriteData(offset_, offset_ + config_.abs_width())) {
+ return;
+ }
+ WriteChar(config_.open_char);
+ WriteNumber(abs_ref.line);
+ WriteChar(kDelimiter);
+ WriteNumber(abs_ref.col);
+ WriteChar(config_.close_char);
+ }
+
+ // Write a relative reference |rel_ref| at |offset|. Note that references
+ // that would overwrite a newline are skipped as this would invalidate all
+ // the other reference line numbers.
+ void Write(offset_t offset, ztf::DeltaLineCol rel_ref) {
+ offset_ = offset;
+ if (!SafeToWriteNumber(rel_ref.line) || !SafeToWriteNumber(rel_ref.col) ||
+ !SafeToWriteData(offset_, offset_ + config_.rel_width())) {
+ return;
+ }
+ WriteChar(config_.open_char);
+ WriteSign(rel_ref.line);
+ WriteNumber(rel_ref.line);
+ WriteChar(kDelimiter);
+ WriteSign(rel_ref.col);
+ WriteNumber(rel_ref.col);
+ WriteChar(config_.close_char);
+ }
+
+ private:
+ // Returns whether it is safe to modify bytes in |[lo, hi)| in |image_| for
+ // Reference correction. Failure cases are:
+ // - Out-of-bound writes.
+ // - Overwriting '\n'. This is a ZTF special case since '\n' dictates file
+ // structure, and Reference correction should never mess with this.
+ bool SafeToWriteData(offset_t lo, offset_t hi) const {
+ DCHECK_LE(lo, hi);
+ // Out of bounds.
+ if (hi > image_.size())
+ return false;
+ for (offset_t i = lo; i < hi; ++i) {
+ if (image_.read<uint8_t>(i) == '\n')
+ return false;
+ }
+ return true;
+ }
+
+ // Checks whether it is safe to write a |val| based on
+ // |config_.digits_per_dim|.
+ bool SafeToWriteNumber(ztf::dim_t val) const {
+ return std::abs(val) < val_bound_;
+ }
+
+ // The Write*() functions each advance |offset_| by a fixed distance. The
+ // caller should ensure there's enough space to write data.
+
+ // Write |character| at |offset_| and increment |offset_|.
+ void WriteChar(uint8_t character) { image_.write(offset_++, character); }
+
+ // Write the sign of |value| at |offset_| and increment |offset_|.
+ void WriteSign(ztf::dim_t value) {
+ image_.write(offset_++,
+ value >= 0 ? ztf::SignChar::kPlus : ztf::SignChar::kMinus);
+ }
+
+ // Writes the absolute value of the number represented by |value| at |offset_|
+ // using zero padding to fill |config_.digits_per_dim|.
+ void WriteNumber(ztf::dim_t value) {
+ size_t size = config_.digits_per_dim + 1;
+ DCHECK_LE(size, kMaxDigitCount + 1);
+ char digits[kMaxDigitCount + 1]; // + 1 for terminator.
+ int len =
+ snprintf(digits, size, "%0*u", config_.digits_per_dim, std::abs(value));
+ DCHECK_EQ(len, config_.digits_per_dim);
+ for (int i = 0; i < len; ++i)
+ image_.write(offset_++, digits[i]);
+ }
+
+ MutableBufferView image_;
+ const ZtfConfig config_;
+ // Bound on numeric values, as limited by |config_.digits_per_dim|.
+ const ztf::dim_t val_bound_;
+ offset_t offset_ = 0;
+ DISALLOW_COPY_AND_ASSIGN(ZtfWriter);
+};
+
+// Specialization of ReferenceReader for reading text references.
+template <typename T>
+class ZtfReferenceReader : public ReferenceReader {
+ public:
+ ZtfReferenceReader(offset_t lo,
+ offset_t hi,
+ ConstBufferView image,
+ const ZtfTranslator& translator,
+ ZtfConfig config)
+ : offset_(lo),
+ hi_(hi),
+ translator_(translator),
+ config_(config),
+ parser_(hi_, image, config_) {
+ DCHECK_LE(hi_, image.size());
+ }
+
+ // Walks |offset_| from |lo| to |hi_| running |parser_|. If any matches are
+ // found they are returned.
+ base::Optional<Reference> GetNext() override {
+ T line_col;
+ for (; offset_ < hi_; ++offset_) {
+ if (!parser_.MatchAtOffset(offset_, &line_col))
+ continue;
+
+ auto target = ConvertToTargetOffset(offset_, line_col);
+ // Ignore targets that point outside the file.
+ if (target == kInvalidOffset)
+ continue;
+ offset_t location = offset_;
+ offset_ += config_.Width(line_col);
+ return Reference{location, target};
+ }
+ return base::nullopt;
+ }
+
+ private:
+ // Converts |lc| (an absolute reference) to an offset using |translator_|.
+ offset_t ConvertToTargetOffset(offset_t /* location */,
+ ztf::LineCol lc) const {
+ return translator_.LineColToOffset(lc);
+ }
+
+ // Converts |dlc| (a relative reference) to an offset using |translator_|.
+ // This requires converting the |dlc| to a ztf::LineCol to find the offset.
+ offset_t ConvertToTargetOffset(offset_t location,
+ ztf::DeltaLineCol dlc) const {
+ auto lc = translator_.OffsetToLineCol(location);
+ if (!lc.has_value())
+ return kInvalidOffset;
+ return translator_.LineColToOffset(lc.value() + dlc);
+ }
+
+ offset_t offset_;
+ const offset_t hi_;
+ const ZtfTranslator& translator_;
+ const ZtfConfig config_;
+ ZtfParser parser_;
+};
+
+// Specialization of ReferenceWriter for writing text references.
+template <typename T>
+class ZtfReferenceWriter : public ReferenceWriter {
+ public:
+ ZtfReferenceWriter(MutableBufferView image,
+ const ZtfTranslator& translator,
+ ZtfConfig config)
+ : translator_(translator), writer_(image, config) {}
+
+ void PutNext(Reference reference) override {
+ T line_col;
+ if (!ConvertToTargetLineCol(reference, &line_col))
+ return;
+
+ writer_.Write(reference.location, line_col);
+ }
+
+ private:
+ // Converts |reference| to an absolute reference to be stored in |out_lc|.
+ // Returns true on success.
+ bool ConvertToTargetLineCol(Reference reference, ztf::LineCol* out_lc) {
+ auto temp_lc = translator_.OffsetToLineCol(reference.target);
+ if (!temp_lc.has_value() && translator_.IsValid(temp_lc.value()))
+ return false;
+
+ *out_lc = temp_lc.value();
+ return true;
+ }
+
+ // Converts |reference| to a relative reference to be stored in |out_dlc|.
+ // Will return true on success.
+ bool ConvertToTargetLineCol(Reference reference, ztf::DeltaLineCol* out_dlc) {
+ auto location_lc = translator_.OffsetToLineCol(reference.location);
+ if (!location_lc.has_value())
+ return false;
+
+ auto target_lc = translator_.OffsetToLineCol(reference.target);
+ if (!target_lc.has_value())
+ return false;
+
+ *out_dlc = target_lc.value() - location_lc.value();
+ return translator_.IsValid(reference.location, *out_dlc);
+ }
+
+ const ZtfTranslator& translator_;
+ ZtfWriter writer_;
+};
+
+// Reads a text header to check for the magic string "ZTxt" at the start
+// indicating the file should be treated as a Zucchini text file.
+bool ReadZtfHeader(ConstBufferView image) {
+ BufferSource source(image);
+ // Reject empty images and "ZTxtxTZ\n" (missing 't').
+ if (source.size() < kTotalMagicSize)
+ return false;
+ return source.CheckNextBytes({'Z', 'T', 'x', 't'});
+}
+
+} // namespace
+
+/******** ZtfTranslator ********/
+
+ZtfTranslator::ZtfTranslator() {}
+
+ZtfTranslator::~ZtfTranslator() = default;
+
+bool ZtfTranslator::Init(ConstBufferView image) {
+ line_starts_.clear();
+ // Record the starting offset of every line in |image_| into |line_start_|.
+ line_starts_.push_back(0);
+ for (size_t i = 0; i < image.size(); ++i) {
+ if (image.read<uint8_t>(i) == '\n') {
+ // Maximum number of entries is |ztf::kMaxDimValue|, including the end
+ // sentinel.
+ if (line_starts_.size() >= ztf::kMaxDimValue)
+ return false;
+ line_starts_.push_back(i + 1);
+ // Check that the line length is reachable from an absolute reference.
+ if (line_starts_.back() - *std::next(line_starts_.rbegin()) >=
+ ztf::kMaxDimValue) {
+ return false;
+ }
+ }
+ }
+ // Since the last character of ZTF file is always '\n', |line_starts_| will
+ // always contain the file length as the last element, which serves as a
+ // sentinel.
+ CHECK_EQ(image.size(), static_cast<size_t>(line_starts_.back()));
+ return true;
+}
+
+bool ZtfTranslator::IsValid(ztf::LineCol lc) const {
+ DCHECK(!line_starts_.empty());
+ return lc.line >= 1 && lc.col >= 1 &&
+ static_cast<offset_t>(lc.line) <= NumLines() &&
+ static_cast<offset_t>(lc.col) <= LineLength(lc.line);
+}
+
+bool ZtfTranslator::IsValid(offset_t offset, ztf::DeltaLineCol dlc) const {
+ DCHECK(!line_starts_.empty());
+ auto abs_lc = OffsetToLineCol(offset);
+ if (!abs_lc.has_value())
+ return false;
+
+ if (!base::CheckAdd(abs_lc->line, dlc.line).IsValid() ||
+ !base::CheckAdd(abs_lc->col, dlc.col).IsValid()) {
+ return false;
+ }
+ return IsValid(abs_lc.value() + dlc);
+}
+
+offset_t ZtfTranslator::LineColToOffset(ztf::LineCol lc) const {
+ // Guard against out of bounds access to |line_starts_| and ensure the
+ // |lc| falls within the file.
+ DCHECK(!line_starts_.empty());
+ if (!IsValid(lc))
+ return kInvalidOffset;
+
+ offset_t target = line_starts_[lc.line - 1] + lc.col - 1;
+ DCHECK_LT(target, line_starts_.back());
+ return target;
+}
+
+base::Optional<ztf::LineCol> ZtfTranslator::OffsetToLineCol(
+ offset_t offset) const {
+ DCHECK(!line_starts_.empty());
+ // Don't place a target outside the image.
+ if (offset >= line_starts_.back())
+ return base::nullopt;
+ auto it = SearchForRange(offset);
+ ztf::LineCol lc;
+ lc.line = std::distance(line_starts_.cbegin(), it) + 1;
+ lc.col = offset - line_starts_[lc.line - 1] + 1;
+ DCHECK_LE(static_cast<offset_t>(lc.col), LineLength(lc.line));
+ return lc;
+}
+
+std::vector<offset_t>::const_iterator ZtfTranslator::SearchForRange(
+ offset_t offset) const {
+ DCHECK(!line_starts_.empty());
+ auto it =
+ std::upper_bound(line_starts_.cbegin(), line_starts_.cend(), offset);
+ DCHECK(it != line_starts_.cbegin());
+ return --it;
+}
+
+offset_t ZtfTranslator::LineLength(uint16_t line) const {
+ DCHECK_GE(line, 1);
+ DCHECK_LE(line, NumLines());
+ return line_starts_[line] - line_starts_[line - 1];
+}
+
+/******** DisassemblerZtf ********/
+
+// Use 2 even though reference "chaining" isn't present in ZTF as it is the
+// usual case for other Disassemblers and this is meant to mimic that as closely
+// as possible.
+DisassemblerZtf::DisassemblerZtf() : Disassembler(2) {}
+
+DisassemblerZtf::~DisassemblerZtf() = default;
+
+// static.
+bool DisassemblerZtf::QuickDetect(ConstBufferView image) {
+ return ReadZtfHeader(image);
+}
+
+ExecutableType DisassemblerZtf::GetExeType() const {
+ return kExeTypeZtf;
+}
+
+std::string DisassemblerZtf::GetExeTypeString() const {
+ return "Zucchini Text Format";
+}
+
+std::vector<ReferenceGroup> DisassemblerZtf::MakeReferenceGroups() const {
+ return {
+ {{5, TypeTag(kAnglesAbs1), PoolTag(kAngles)},
+ &DisassemblerZtf::MakeReadAbs<1, kAngles>,
+ &DisassemblerZtf::MakeWriteAbs<1, kAngles>},
+ {{7, TypeTag(kAnglesAbs2), PoolTag(kAngles)},
+ &DisassemblerZtf::MakeReadAbs<2, kAngles>,
+ &DisassemblerZtf::MakeWriteAbs<2, kAngles>},
+ {{9, TypeTag(kAnglesAbs3), PoolTag(kAngles)},
+ &DisassemblerZtf::MakeReadAbs<3, kAngles>,
+ &DisassemblerZtf::MakeWriteAbs<3, kAngles>},
+ {{7, TypeTag(kAnglesRel1), PoolTag(kAngles)},
+ &DisassemblerZtf::MakeReadRel<1, kAngles>,
+ &DisassemblerZtf::MakeWriteRel<1, kAngles>},
+ {{9, TypeTag(kAnglesRel2), PoolTag(kAngles)},
+ &DisassemblerZtf::MakeReadRel<2, kAngles>,
+ &DisassemblerZtf::MakeWriteRel<2, kAngles>},
+ {{11, TypeTag(kAnglesRel3), PoolTag(kAngles)},
+ &DisassemblerZtf::MakeReadRel<3, kAngles>,
+ &DisassemblerZtf::MakeWriteRel<3, kAngles>},
+ {{5, TypeTag(kBracesAbs1), PoolTag(kBraces)},
+ &DisassemblerZtf::MakeReadAbs<1, kBraces>,
+ &DisassemblerZtf::MakeWriteAbs<1, kBraces>},
+ {{7, TypeTag(kBracesAbs2), PoolTag(kBraces)},
+ &DisassemblerZtf::MakeReadAbs<2, kBraces>,
+ &DisassemblerZtf::MakeWriteAbs<2, kBraces>},
+ {{9, TypeTag(kBracesAbs3), PoolTag(kBraces)},
+ &DisassemblerZtf::MakeReadAbs<3, kBraces>,
+ &DisassemblerZtf::MakeWriteAbs<3, kBraces>},
+ {{7, TypeTag(kBracesRel1), PoolTag(kBraces)},
+ &DisassemblerZtf::MakeReadRel<1, kBraces>,
+ &DisassemblerZtf::MakeWriteRel<1, kBraces>},
+ {{9, TypeTag(kBracesRel2), PoolTag(kBraces)},
+ &DisassemblerZtf::MakeReadRel<2, kBraces>,
+ &DisassemblerZtf::MakeWriteRel<2, kBraces>},
+ {{11, TypeTag(kBracesRel3), PoolTag(kBraces)},
+ &DisassemblerZtf::MakeReadRel<3, kBraces>,
+ &DisassemblerZtf::MakeWriteRel<3, kBraces>},
+ {{5, TypeTag(kBracketsAbs1), PoolTag(kBrackets)},
+ &DisassemblerZtf::MakeReadAbs<1, kBrackets>,
+ &DisassemblerZtf::MakeWriteAbs<1, kBrackets>},
+ {{7, TypeTag(kBracketsAbs2), PoolTag(kBrackets)},
+ &DisassemblerZtf::MakeReadAbs<2, kBrackets>,
+ &DisassemblerZtf::MakeWriteAbs<2, kBrackets>},
+ {{9, TypeTag(kBracketsAbs3), PoolTag(kBrackets)},
+ &DisassemblerZtf::MakeReadAbs<3, kBrackets>,
+ &DisassemblerZtf::MakeWriteAbs<3, kBrackets>},
+ {{7, TypeTag(kBracketsRel1), PoolTag(kBrackets)},
+ &DisassemblerZtf::MakeReadRel<1, kBrackets>,
+ &DisassemblerZtf::MakeWriteRel<1, kBrackets>},
+ {{9, TypeTag(kBracketsRel2), PoolTag(kBrackets)},
+ &DisassemblerZtf::MakeReadRel<2, kBrackets>,
+ &DisassemblerZtf::MakeWriteRel<2, kBrackets>},
+ {{11, TypeTag(kBracketsRel3), PoolTag(kBrackets)},
+ &DisassemblerZtf::MakeReadRel<3, kBrackets>,
+ &DisassemblerZtf::MakeWriteRel<3, kBrackets>},
+ {{5, TypeTag(kParenthesesAbs1), PoolTag(kParentheses)},
+ &DisassemblerZtf::MakeReadAbs<1, kParentheses>,
+ &DisassemblerZtf::MakeWriteAbs<1, kParentheses>},
+ {{7, TypeTag(kParenthesesAbs2), PoolTag(kParentheses)},
+ &DisassemblerZtf::MakeReadAbs<2, kParentheses>,
+ &DisassemblerZtf::MakeWriteAbs<2, kParentheses>},
+ {{9, TypeTag(kParenthesesAbs3), PoolTag(kParentheses)},
+ &DisassemblerZtf::MakeReadAbs<3, kParentheses>,
+ &DisassemblerZtf::MakeWriteAbs<3, kParentheses>},
+ {{7, TypeTag(kParenthesesRel1), PoolTag(kParentheses)},
+ &DisassemblerZtf::MakeReadRel<1, kParentheses>,
+ &DisassemblerZtf::MakeWriteRel<1, kParentheses>},
+ {{9, TypeTag(kParenthesesRel2), PoolTag(kParentheses)},
+ &DisassemblerZtf::MakeReadRel<2, kParentheses>,
+ &DisassemblerZtf::MakeWriteRel<2, kParentheses>},
+ {{11, TypeTag(kParenthesesRel3), PoolTag(kParentheses)},
+ &DisassemblerZtf::MakeReadRel<3, kParentheses>,
+ &DisassemblerZtf::MakeWriteRel<3, kParentheses>},
+ };
+}
+
+template <uint8_t digits, DisassemblerZtf::ReferencePool pool>
+std::unique_ptr<ReferenceReader> DisassemblerZtf::MakeReadAbs(offset_t lo,
+ offset_t hi) {
+ static_assert(digits >= 1 && digits <= kMaxDigitCount,
+ "|digits| must be in range [1, 3]");
+ return std::make_unique<ZtfReferenceReader<ztf::LineCol>>(
+ lo, hi, image_, translator_, MakeZtfConfig<pool>(digits));
+}
+
+template <uint8_t digits, DisassemblerZtf::ReferencePool pool>
+std::unique_ptr<ReferenceReader> DisassemblerZtf::MakeReadRel(offset_t lo,
+ offset_t hi) {
+ static_assert(digits >= 1 && digits <= kMaxDigitCount,
+ "|digits| must be in range [1, 3]");
+ return std::make_unique<ZtfReferenceReader<ztf::DeltaLineCol>>(
+ lo, hi, image_, translator_, MakeZtfConfig<pool>(digits));
+}
+
+template <uint8_t digits, DisassemblerZtf::ReferencePool pool>
+std::unique_ptr<ReferenceWriter> DisassemblerZtf::MakeWriteAbs(
+ MutableBufferView image) {
+ static_assert(digits >= 1 && digits <= kMaxDigitCount,
+ "|digits| must be in range [1, 3]");
+ return std::make_unique<ZtfReferenceWriter<ztf::LineCol>>(
+ image, translator_, MakeZtfConfig<pool>(digits));
+}
+
+template <uint8_t digits, DisassemblerZtf::ReferencePool pool>
+std::unique_ptr<ReferenceWriter> DisassemblerZtf::MakeWriteRel(
+ MutableBufferView image) {
+ static_assert(digits >= 1 && digits <= kMaxDigitCount,
+ "|digits| must be in range [1, 3]");
+ return std::make_unique<ZtfReferenceWriter<ztf::DeltaLineCol>>(
+ image, translator_, MakeZtfConfig<pool>(digits));
+}
+
+bool DisassemblerZtf::Parse(ConstBufferView image) {
+ image_ = image;
+ if (!ReadZtfHeader(image_))
+ return false;
+
+ CHECK_GE(image_.size(),
+ static_cast<size_t>(kTotalMagicSize)); // Needs header and footer.
+
+ // Find the terminating footer "txTZ\n" that indicates the end of the image.
+ offset_t offset = 0;
+ for (; offset <= image_.size() - kFooterMagicSize; offset++) {
+ if (image_.read<uint8_t>(offset) == 't' &&
+ image_.read<uint8_t>(offset + 1) == 'x' &&
+ image_.read<uint8_t>(offset + 2) == 'T' &&
+ image_.read<uint8_t>(offset + 3) == 'Z' &&
+ image_.read<uint8_t>(offset + 4) == '\n') {
+ break;
+ }
+ }
+
+ // If no footer is found before the end of the image then the parsing failed.
+ if (offset > image_.size() - kFooterMagicSize)
+ return false;
+ image_.shrink(offset + kFooterMagicSize);
+
+ return translator_.Init(image_);
+}
+
+} // namespace zucchini