xref: /aosp_15_r20/external/zucchini/disassembler_ztf.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/zucchini/disassembler_ztf.h"
6 
7 #include <algorithm>
8 #include <cmath>
9 #include <iterator>
10 #include <limits>
11 #include <numeric>
12 
13 #include "base/check_op.h"
14 #include "base/numerics/checked_math.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "components/zucchini/algorithm.h"
17 #include "components/zucchini/buffer_source.h"
18 #include "components/zucchini/buffer_view.h"
19 #include "components/zucchini/io_utils.h"
20 
21 namespace zucchini {
22 
23 namespace {
24 
25 constexpr uint8_t kDelimiter = ',';
26 
27 constexpr int kHeaderMagicSize = 4;
28 constexpr int kFooterMagicSize = 5;
29 constexpr int kTotalMagicSize = kHeaderMagicSize + kFooterMagicSize;
30 
31 // Number of characters that aren't digits in each type of reference.
32 constexpr int kNumConstCharInAbs = 3;
33 constexpr int kNumConstCharInRel = 5;
34 
35 /******** ZtfConfig ********/
36 
37 // For passing around metadata about the type of reference to match.
38 // - |digits_per_dim| is the length of the offset in lines/cols of a
39 //   reference.
40 // - |open_char| is an ASCII character representing the opening char.
41 // - |close_char| is an ASCII character representing the closing char.
42 struct ZtfConfig {
43   uint8_t digits_per_dim;
44   uint8_t open_char;
45   uint8_t close_char;
46 
abs_widthzucchini::__anond77e67580111::ZtfConfig47   constexpr uint8_t abs_width() const {
48     return digits_per_dim * 2 + kNumConstCharInAbs;
49   }
50 
rel_widthzucchini::__anond77e67580111::ZtfConfig51   constexpr uint8_t rel_width() const {
52     return digits_per_dim * 2 + kNumConstCharInRel;
53   }
54 
Widthzucchini::__anond77e67580111::ZtfConfig55   uint8_t Width(ztf::LineCol /* lc */) const { return abs_width(); }
56 
Widthzucchini::__anond77e67580111::ZtfConfig57   uint8_t Width(ztf::DeltaLineCol /* dlc */) const { return rel_width(); }
58 };
59 
60 // Creates a ZtfConfig for parsing or writing based on the desired |digits| and
61 // |pool|.
62 template <DisassemblerZtf::ReferencePool pool>
MakeZtfConfig(uint8_t digits)63 constexpr ZtfConfig MakeZtfConfig(uint8_t digits) {
64   switch (pool) {
65     case DisassemblerZtf::kAngles:
66       return ZtfConfig{digits, '<', '>'};
67     case DisassemblerZtf::kBraces:
68       return ZtfConfig{digits, '{', '}'};
69     case DisassemblerZtf::kBrackets:
70       return ZtfConfig{digits, '[', ']'};
71     case DisassemblerZtf::kParentheses:
72       break;  // Handled below.
73   }
74   return ZtfConfig{digits, '(', ')'};
75 }
76 
77 /******** ZtfParser ********/
78 
79 // ZtfParser is used to extract (absolute) LineCol and (relative) DeltaLineCol
80 // from a ZTF file, and contains various helpers for character, digits, and sign
81 // matching.
82 class ZtfParser {
83  public:
ZtfParser(offset_t hi,ConstBufferView image,ZtfConfig config)84   ZtfParser(offset_t hi, ConstBufferView image, ZtfConfig config)
85       : image_(image), hi_(hi), config_(config) {
86     DCHECK_LE(static_cast<size_t>(std::pow(10U, config_.digits_per_dim)),
87               ztf::kMaxDimValue);
88   }
89 
90   ZtfParser(const ZtfParser&) = delete;
91   const ZtfParser& operator=(const ZtfParser&) = delete;
92 
93   // Attempts to match an absolute reference at |offset|. If successful then
94   // assigns the result to |abs_lc| and returns true. Otherwise returns false.
95   // An absolute reference takes the form:
96   // <open><digits><delimiter><digits><close>
MatchAtOffset(offset_t offset,ztf::LineCol * abs_lc)97   bool MatchAtOffset(offset_t offset, ztf::LineCol* abs_lc) {
98     if (hi_ < config_.abs_width() || offset > hi_ - config_.abs_width())
99       return false;
100     offset_ = offset;
101     return MatchChar(config_.open_char) && MatchDigits(+1, &abs_lc->line) &&
102            MatchChar(kDelimiter) && MatchDigits(+1, &abs_lc->col) &&
103            MatchChar(config_.close_char);
104   }
105 
106   // Attempts to match an absolute reference at |offset|. If successful then
107   // assigns the result to |rel_lc| and returns true. Otherwise returns false. A
108   // relative reference takes the form:
109   // <open><sign><digits><delimiter><sign><digits><close>
MatchAtOffset(offset_t offset,ztf::DeltaLineCol * rel_dlc)110   bool MatchAtOffset(offset_t offset, ztf::DeltaLineCol* rel_dlc) {
111     if (hi_ < config_.rel_width() || offset > hi_ - config_.rel_width())
112       return false;
113     offset_ = offset;
114     ztf::dim_t line_sign;
115     ztf::dim_t col_sign;
116     return MatchChar(config_.open_char) && MatchSign(&line_sign) &&
117            MatchDigits(line_sign, &rel_dlc->line) && MatchChar(kDelimiter) &&
118            MatchSign(&col_sign) && MatchDigits(col_sign, &rel_dlc->col) &&
119            MatchChar(config_.close_char);
120   }
121 
122  private:
123   // The Match*() functions below can advance |offset_|, and return a bool to
124   // indicate success to allow chaining using &&.
125 
126   // Returns true if |character| is at location |offset_| in |image_| and
127   // increments |offset_|.
MatchChar(uint8_t character)128   bool MatchChar(uint8_t character) {
129     return character == image_.read<uint8_t>(offset_++);
130   }
131 
132   // Looks for '+' or '-' at |offset_|. If found, stores +1 or -1 in |sign| and
133   // returns true. Otherwise returns false.
MatchSign(ztf::dim_t * sign)134   bool MatchSign(ztf::dim_t* sign) {
135     uint8_t val = image_.read<uint8_t>(offset_++);
136     if (val == static_cast<uint8_t>(ztf::SignChar::kMinus)) {
137       *sign = -1;
138       return true;
139     }
140     if (val == static_cast<uint8_t>(ztf::SignChar::kPlus)) {
141       *sign = 1;
142       return true;
143     }
144     return false;
145   }
146 
147   // Attempts to extract a number with the number of base 10 digits equal to
148   // |config_.digits_per_dim| from |image_| starting from |offset_|. Returns
149   // true and assigns the integer value to |value| if successful.
MatchDigits(ztf::dim_t sign,ztf::dim_t * value)150   bool MatchDigits(ztf::dim_t sign, ztf::dim_t* value) {
151     ztf::dim_t output = 0;
152     for (int i = 0; i < config_.digits_per_dim; ++i) {
153       auto digit = image_.read<uint8_t>(offset_++);
154       if (digit >= '0' && digit < '0' + 10)
155         output = output * 10 + digit - '0';
156       else
157         return false;
158     }
159     if (!output && sign < 0)  // Disallow "-0", "-00", etc.
160       return false;
161     *value = sign * output;
162     return true;
163   }
164 
165   ConstBufferView image_;
166   const offset_t hi_;
167   const ZtfConfig config_;
168   offset_t offset_ = 0;
169 };
170 
171 /******** ZtfWriter ********/
172 
173 // ZtfWriter is used to write references to an image. This includes writing
174 // the enclosing characters around the reference.
175 class ZtfWriter {
176  public:
ZtfWriter(MutableBufferView image,ZtfConfig config)177   ZtfWriter(MutableBufferView image, ZtfConfig config)
178       : image_(image),
179         config_(config),
180         val_bound_(
181             static_cast<ztf::dim_t>(std::pow(10, config_.digits_per_dim))) {}
182 
183   ZtfWriter(const ZtfWriter&) = delete;
184   const ZtfWriter& operator=(const ZtfWriter&) = delete;
185 
186   // Write an absolute reference |abs_ref| at |offset|. Note that references
187   // that would overwrite a newline are skipped as this would invalidate all
188   // the other reference line numbers.
Write(offset_t offset,ztf::LineCol abs_ref)189   void Write(offset_t offset, ztf::LineCol abs_ref) {
190     offset_ = offset;
191     if (!SafeToWriteNumber(abs_ref.line) || !SafeToWriteNumber(abs_ref.col) ||
192         !SafeToWriteData(offset_, offset_ + config_.abs_width())) {
193       return;
194     }
195     WriteChar(config_.open_char);
196     WriteNumber(abs_ref.line);
197     WriteChar(kDelimiter);
198     WriteNumber(abs_ref.col);
199     WriteChar(config_.close_char);
200   }
201 
202   // Write a relative reference |rel_ref| at |offset|. Note that references
203   // that would overwrite a newline are skipped as this would invalidate all
204   // the other reference line numbers.
Write(offset_t offset,ztf::DeltaLineCol rel_ref)205   void Write(offset_t offset, ztf::DeltaLineCol rel_ref) {
206     offset_ = offset;
207     if (!SafeToWriteNumber(rel_ref.line) || !SafeToWriteNumber(rel_ref.col) ||
208         !SafeToWriteData(offset_, offset_ + config_.rel_width())) {
209       return;
210     }
211     WriteChar(config_.open_char);
212     WriteSign(rel_ref.line);
213     WriteNumber(rel_ref.line);
214     WriteChar(kDelimiter);
215     WriteSign(rel_ref.col);
216     WriteNumber(rel_ref.col);
217     WriteChar(config_.close_char);
218   }
219 
220  private:
221   // Returns whether it is safe to modify bytes in |[lo, hi)| in |image_| for
222   // Reference correction. Failure cases are:
223   // - Out-of-bound writes.
224   // - Overwriting '\n'. This is a ZTF special case since '\n' dictates file
225   //   structure, and Reference correction should never mess with this.
SafeToWriteData(offset_t lo,offset_t hi) const226   bool SafeToWriteData(offset_t lo, offset_t hi) const {
227     DCHECK_LE(lo, hi);
228     // Out of bounds.
229     if (hi > image_.size())
230       return false;
231     for (offset_t i = lo; i < hi; ++i) {
232       if (image_.read<uint8_t>(i) == '\n')
233         return false;
234     }
235     return true;
236   }
237 
238   // Checks whether it is safe to write a |val| based on
239   // |config_.digits_per_dim|.
SafeToWriteNumber(ztf::dim_t val) const240   bool SafeToWriteNumber(ztf::dim_t val) const {
241     return std::abs(val) < val_bound_;
242   }
243 
244   // The Write*() functions each advance |offset_| by a fixed distance. The
245   // caller should ensure there's enough space to write data.
246 
247   // Write |character| at |offset_| and increment |offset_|.
WriteChar(uint8_t character)248   void WriteChar(uint8_t character) { image_.write(offset_++, character); }
249 
250   // Write the sign of |value| at |offset_| and increment |offset_|.
WriteSign(ztf::dim_t value)251   void WriteSign(ztf::dim_t value) {
252     image_.write(offset_++,
253                  value >= 0 ? ztf::SignChar::kPlus : ztf::SignChar::kMinus);
254   }
255 
256   // Writes the absolute value of the number represented by |value| at |offset_|
257   // using zero padding to fill |config_.digits_per_dim|.
WriteNumber(ztf::dim_t value)258   void WriteNumber(ztf::dim_t value) {
259     size_t size = config_.digits_per_dim + 1;
260     DCHECK_LE(size, kMaxDigitCount + 1);
261     char digits[kMaxDigitCount + 1];  // + 1 for terminator.
262     int len =
263         snprintf(digits, size, "%0*u", config_.digits_per_dim, std::abs(value));
264     DCHECK_EQ(len, config_.digits_per_dim);
265     for (int i = 0; i < len; ++i)
266       image_.write(offset_++, digits[i]);
267   }
268 
269   MutableBufferView image_;
270   const ZtfConfig config_;
271   // Bound on numeric values, as limited by |config_.digits_per_dim|.
272   const ztf::dim_t val_bound_;
273   offset_t offset_ = 0;
274 };
275 
276 // Specialization of ReferenceReader for reading text references.
277 template <typename T>
278 class ZtfReferenceReader : public ReferenceReader {
279  public:
ZtfReferenceReader(offset_t lo,offset_t hi,ConstBufferView image,const ZtfTranslator & translator,ZtfConfig config)280   ZtfReferenceReader(offset_t lo,
281                      offset_t hi,
282                      ConstBufferView image,
283                      const ZtfTranslator& translator,
284                      ZtfConfig config)
285       : offset_(lo),
286         hi_(hi),
287         translator_(translator),
288         config_(config),
289         parser_(hi_, image, config_) {
290     DCHECK_LE(hi_, image.size());
291   }
292 
293   // Walks |offset_| from |lo| to |hi_| running |parser_|. If any matches are
294   // found they are returned.
GetNext()295   std::optional<Reference> GetNext() override {
296     T line_col;
297     for (; offset_ < hi_; ++offset_) {
298       if (!parser_.MatchAtOffset(offset_, &line_col))
299         continue;
300 
301       auto target = ConvertToTargetOffset(offset_, line_col);
302       // Ignore targets that point outside the file.
303       if (target == kInvalidOffset)
304         continue;
305       offset_t location = offset_;
306       offset_ += config_.Width(line_col);
307       return Reference{location, target};
308     }
309     return std::nullopt;
310   }
311 
312  private:
313   // Converts |lc| (an absolute reference) to an offset using |translator_|.
ConvertToTargetOffset(offset_t,ztf::LineCol lc) const314   offset_t ConvertToTargetOffset(offset_t /* location */,
315                                  ztf::LineCol lc) const {
316     return translator_.LineColToOffset(lc);
317   }
318 
319   // Converts |dlc| (a relative reference) to an offset using |translator_|.
320   // This requires converting the |dlc| to a ztf::LineCol to find the offset.
ConvertToTargetOffset(offset_t location,ztf::DeltaLineCol dlc) const321   offset_t ConvertToTargetOffset(offset_t location,
322                                  ztf::DeltaLineCol dlc) const {
323     auto lc = translator_.OffsetToLineCol(location);
324     if (!lc.has_value())
325       return kInvalidOffset;
326     return translator_.LineColToOffset(lc.value() + dlc);
327   }
328 
329   offset_t offset_;
330   const offset_t hi_;
331   const ZtfTranslator& translator_;
332   const ZtfConfig config_;
333   ZtfParser parser_;
334 };
335 
336 // Specialization of ReferenceWriter for writing text references.
337 template <typename T>
338 class ZtfReferenceWriter : public ReferenceWriter {
339  public:
ZtfReferenceWriter(MutableBufferView image,const ZtfTranslator & translator,ZtfConfig config)340   ZtfReferenceWriter(MutableBufferView image,
341                      const ZtfTranslator& translator,
342                      ZtfConfig config)
343       : translator_(translator), writer_(image, config) {}
344 
PutNext(Reference reference)345   void PutNext(Reference reference) override {
346     T line_col;
347     if (!ConvertToTargetLineCol(reference, &line_col))
348       return;
349 
350     writer_.Write(reference.location, line_col);
351   }
352 
353  private:
354   // Converts |reference| to an absolute reference to be stored in |out_lc|.
355   // Returns true on success.
ConvertToTargetLineCol(Reference reference,ztf::LineCol * out_lc)356   bool ConvertToTargetLineCol(Reference reference, ztf::LineCol* out_lc) {
357     auto temp_lc = translator_.OffsetToLineCol(reference.target);
358     if (!temp_lc.has_value() || !translator_.IsValid(temp_lc.value()))
359       return false;
360 
361     *out_lc = temp_lc.value();
362     return true;
363   }
364 
365   // Converts |reference| to a relative reference to be stored in |out_dlc|.
366   // Will return true on success.
ConvertToTargetLineCol(Reference reference,ztf::DeltaLineCol * out_dlc)367   bool ConvertToTargetLineCol(Reference reference, ztf::DeltaLineCol* out_dlc) {
368     auto location_lc = translator_.OffsetToLineCol(reference.location);
369     if (!location_lc.has_value())
370       return false;
371 
372     auto target_lc = translator_.OffsetToLineCol(reference.target);
373     if (!target_lc.has_value())
374       return false;
375 
376     *out_dlc = target_lc.value() - location_lc.value();
377     return translator_.IsValid(reference.location, *out_dlc);
378   }
379 
380   const ZtfTranslator& translator_;
381   ZtfWriter writer_;
382 };
383 
384 // Reads a text header to check for the magic string "ZTxt" at the start
385 // indicating the file should be treated as a Zucchini text file.
ReadZtfHeader(ConstBufferView image)386 bool ReadZtfHeader(ConstBufferView image) {
387   BufferSource source(image);
388   // Reject empty images and "ZTxtxTZ\n" (missing 't').
389   if (source.size() < kTotalMagicSize)
390     return false;
391   if (source.size() > std::numeric_limits<offset_t>::max())
392     return false;
393   return source.CheckNextBytes({'Z', 'T', 'x', 't'});
394 }
395 
396 }  // namespace
397 
398 /******** ZtfTranslator ********/
399 
ZtfTranslator()400 ZtfTranslator::ZtfTranslator() {}
401 
402 ZtfTranslator::~ZtfTranslator() = default;
403 
Init(ConstBufferView image)404 bool ZtfTranslator::Init(ConstBufferView image) {
405   line_starts_.clear();
406   // Record the starting offset of every line in |image_| into |line_start_|.
407   line_starts_.push_back(0);
408   for (size_t i = 0; i < image.size(); ++i) {
409     if (image.read<uint8_t>(i) == '\n') {
410       // Maximum number of entries is |ztf::kMaxDimValue|, including the end
411       // sentinel.
412       if (line_starts_.size() >= ztf::kMaxDimValue)
413         return false;
414       line_starts_.push_back(base::checked_cast<offset_t>(i + 1));
415       // Check that the line length is reachable from an absolute reference.
416       if (line_starts_.back() - *std::next(line_starts_.rbegin()) >=
417           ztf::kMaxDimValue) {
418         return false;
419       }
420     }
421   }
422   // Since the last character of ZTF file is always '\n', |line_starts_| will
423   // always contain the file length as the last element, which serves as a
424   // sentinel.
425   CHECK_EQ(image.size(), static_cast<size_t>(line_starts_.back()));
426   return true;
427 }
428 
IsValid(ztf::LineCol lc) const429 bool ZtfTranslator::IsValid(ztf::LineCol lc) const {
430   DCHECK(!line_starts_.empty());
431   return lc.line >= 1 && lc.col >= 1 &&
432          static_cast<offset_t>(lc.line) <= NumLines() &&
433          static_cast<offset_t>(lc.col) <= LineLength(lc.line);
434 }
435 
IsValid(offset_t offset,ztf::DeltaLineCol dlc) const436 bool ZtfTranslator::IsValid(offset_t offset, ztf::DeltaLineCol dlc) const {
437   DCHECK(!line_starts_.empty());
438   auto abs_lc = OffsetToLineCol(offset);
439   if (!abs_lc.has_value())
440     return false;
441 
442   if (!base::CheckAdd(abs_lc->line, dlc.line).IsValid() ||
443       !base::CheckAdd(abs_lc->col, dlc.col).IsValid()) {
444     return false;
445   }
446   return IsValid(abs_lc.value() + dlc);
447 }
448 
LineColToOffset(ztf::LineCol lc) const449 offset_t ZtfTranslator::LineColToOffset(ztf::LineCol lc) const {
450   // Guard against out of bounds access to |line_starts_| and ensure the
451   // |lc| falls within the file.
452   DCHECK(!line_starts_.empty());
453   if (!IsValid(lc))
454     return kInvalidOffset;
455 
456   offset_t target = line_starts_[lc.line - 1] + lc.col - 1;
457   DCHECK_LT(target, line_starts_.back());
458   return target;
459 }
460 
OffsetToLineCol(offset_t offset) const461 std::optional<ztf::LineCol> ZtfTranslator::OffsetToLineCol(
462     offset_t offset) const {
463   DCHECK(!line_starts_.empty());
464   // Don't place a target outside the image.
465   if (offset >= line_starts_.back())
466     return std::nullopt;
467   auto it = SearchForRange(offset);
468   ztf::LineCol lc;
469   lc.line = std::distance(line_starts_.cbegin(), it) + 1;
470   lc.col = offset - line_starts_[lc.line - 1] + 1;
471   DCHECK_LE(static_cast<offset_t>(lc.col), LineLength(lc.line));
472   return lc;
473 }
474 
SearchForRange(offset_t offset) const475 std::vector<offset_t>::const_iterator ZtfTranslator::SearchForRange(
476     offset_t offset) const {
477   DCHECK(!line_starts_.empty());
478   auto it =
479       std::upper_bound(line_starts_.cbegin(), line_starts_.cend(), offset);
480   DCHECK(it != line_starts_.cbegin());
481   return --it;
482 }
483 
LineLength(uint16_t line) const484 offset_t ZtfTranslator::LineLength(uint16_t line) const {
485   DCHECK_GE(line, 1);
486   DCHECK_LE(line, NumLines());
487   return line_starts_[line] - line_starts_[line - 1];
488 }
489 
490 /******** DisassemblerZtf ********/
491 
492 // Use 2 even though reference "chaining" isn't present in ZTF as it is the
493 // usual case for other Disassemblers and this is meant to mimic that as closely
494 // as possible.
DisassemblerZtf()495 DisassemblerZtf::DisassemblerZtf() : Disassembler(2) {}
496 
497 DisassemblerZtf::~DisassemblerZtf() = default;
498 
499 // static.
QuickDetect(ConstBufferView image)500 bool DisassemblerZtf::QuickDetect(ConstBufferView image) {
501   return ReadZtfHeader(image);
502 }
503 
GetExeType() const504 ExecutableType DisassemblerZtf::GetExeType() const {
505   return kExeTypeZtf;
506 }
507 
GetExeTypeString() const508 std::string DisassemblerZtf::GetExeTypeString() const {
509   return "Zucchini Text Format";
510 }
511 
MakeReferenceGroups() const512 std::vector<ReferenceGroup> DisassemblerZtf::MakeReferenceGroups() const {
513   return {
514       {{5, TypeTag(kAnglesAbs1), PoolTag(kAngles)},
515        &DisassemblerZtf::MakeReadAbs<1, kAngles>,
516        &DisassemblerZtf::MakeWriteAbs<1, kAngles>},
517       {{7, TypeTag(kAnglesAbs2), PoolTag(kAngles)},
518        &DisassemblerZtf::MakeReadAbs<2, kAngles>,
519        &DisassemblerZtf::MakeWriteAbs<2, kAngles>},
520       {{9, TypeTag(kAnglesAbs3), PoolTag(kAngles)},
521        &DisassemblerZtf::MakeReadAbs<3, kAngles>,
522        &DisassemblerZtf::MakeWriteAbs<3, kAngles>},
523       {{7, TypeTag(kAnglesRel1), PoolTag(kAngles)},
524        &DisassemblerZtf::MakeReadRel<1, kAngles>,
525        &DisassemblerZtf::MakeWriteRel<1, kAngles>},
526       {{9, TypeTag(kAnglesRel2), PoolTag(kAngles)},
527        &DisassemblerZtf::MakeReadRel<2, kAngles>,
528        &DisassemblerZtf::MakeWriteRel<2, kAngles>},
529       {{11, TypeTag(kAnglesRel3), PoolTag(kAngles)},
530        &DisassemblerZtf::MakeReadRel<3, kAngles>,
531        &DisassemblerZtf::MakeWriteRel<3, kAngles>},
532       {{5, TypeTag(kBracesAbs1), PoolTag(kBraces)},
533        &DisassemblerZtf::MakeReadAbs<1, kBraces>,
534        &DisassemblerZtf::MakeWriteAbs<1, kBraces>},
535       {{7, TypeTag(kBracesAbs2), PoolTag(kBraces)},
536        &DisassemblerZtf::MakeReadAbs<2, kBraces>,
537        &DisassemblerZtf::MakeWriteAbs<2, kBraces>},
538       {{9, TypeTag(kBracesAbs3), PoolTag(kBraces)},
539        &DisassemblerZtf::MakeReadAbs<3, kBraces>,
540        &DisassemblerZtf::MakeWriteAbs<3, kBraces>},
541       {{7, TypeTag(kBracesRel1), PoolTag(kBraces)},
542        &DisassemblerZtf::MakeReadRel<1, kBraces>,
543        &DisassemblerZtf::MakeWriteRel<1, kBraces>},
544       {{9, TypeTag(kBracesRel2), PoolTag(kBraces)},
545        &DisassemblerZtf::MakeReadRel<2, kBraces>,
546        &DisassemblerZtf::MakeWriteRel<2, kBraces>},
547       {{11, TypeTag(kBracesRel3), PoolTag(kBraces)},
548        &DisassemblerZtf::MakeReadRel<3, kBraces>,
549        &DisassemblerZtf::MakeWriteRel<3, kBraces>},
550       {{5, TypeTag(kBracketsAbs1), PoolTag(kBrackets)},
551        &DisassemblerZtf::MakeReadAbs<1, kBrackets>,
552        &DisassemblerZtf::MakeWriteAbs<1, kBrackets>},
553       {{7, TypeTag(kBracketsAbs2), PoolTag(kBrackets)},
554        &DisassemblerZtf::MakeReadAbs<2, kBrackets>,
555        &DisassemblerZtf::MakeWriteAbs<2, kBrackets>},
556       {{9, TypeTag(kBracketsAbs3), PoolTag(kBrackets)},
557        &DisassemblerZtf::MakeReadAbs<3, kBrackets>,
558        &DisassemblerZtf::MakeWriteAbs<3, kBrackets>},
559       {{7, TypeTag(kBracketsRel1), PoolTag(kBrackets)},
560        &DisassemblerZtf::MakeReadRel<1, kBrackets>,
561        &DisassemblerZtf::MakeWriteRel<1, kBrackets>},
562       {{9, TypeTag(kBracketsRel2), PoolTag(kBrackets)},
563        &DisassemblerZtf::MakeReadRel<2, kBrackets>,
564        &DisassemblerZtf::MakeWriteRel<2, kBrackets>},
565       {{11, TypeTag(kBracketsRel3), PoolTag(kBrackets)},
566        &DisassemblerZtf::MakeReadRel<3, kBrackets>,
567        &DisassemblerZtf::MakeWriteRel<3, kBrackets>},
568       {{5, TypeTag(kParenthesesAbs1), PoolTag(kParentheses)},
569        &DisassemblerZtf::MakeReadAbs<1, kParentheses>,
570        &DisassemblerZtf::MakeWriteAbs<1, kParentheses>},
571       {{7, TypeTag(kParenthesesAbs2), PoolTag(kParentheses)},
572        &DisassemblerZtf::MakeReadAbs<2, kParentheses>,
573        &DisassemblerZtf::MakeWriteAbs<2, kParentheses>},
574       {{9, TypeTag(kParenthesesAbs3), PoolTag(kParentheses)},
575        &DisassemblerZtf::MakeReadAbs<3, kParentheses>,
576        &DisassemblerZtf::MakeWriteAbs<3, kParentheses>},
577       {{7, TypeTag(kParenthesesRel1), PoolTag(kParentheses)},
578        &DisassemblerZtf::MakeReadRel<1, kParentheses>,
579        &DisassemblerZtf::MakeWriteRel<1, kParentheses>},
580       {{9, TypeTag(kParenthesesRel2), PoolTag(kParentheses)},
581        &DisassemblerZtf::MakeReadRel<2, kParentheses>,
582        &DisassemblerZtf::MakeWriteRel<2, kParentheses>},
583       {{11, TypeTag(kParenthesesRel3), PoolTag(kParentheses)},
584        &DisassemblerZtf::MakeReadRel<3, kParentheses>,
585        &DisassemblerZtf::MakeWriteRel<3, kParentheses>},
586   };
587 }
588 
589 template <uint8_t digits, DisassemblerZtf::ReferencePool pool>
MakeReadAbs(offset_t lo,offset_t hi)590 std::unique_ptr<ReferenceReader> DisassemblerZtf::MakeReadAbs(offset_t lo,
591                                                               offset_t hi) {
592   static_assert(digits >= 1 && digits <= kMaxDigitCount,
593                 "|digits| must be in range [1, 3]");
594   return std::make_unique<ZtfReferenceReader<ztf::LineCol>>(
595       lo, hi, image_, translator_, MakeZtfConfig<pool>(digits));
596 }
597 
598 template <uint8_t digits, DisassemblerZtf::ReferencePool pool>
MakeReadRel(offset_t lo,offset_t hi)599 std::unique_ptr<ReferenceReader> DisassemblerZtf::MakeReadRel(offset_t lo,
600                                                               offset_t hi) {
601   static_assert(digits >= 1 && digits <= kMaxDigitCount,
602                 "|digits| must be in range [1, 3]");
603   return std::make_unique<ZtfReferenceReader<ztf::DeltaLineCol>>(
604       lo, hi, image_, translator_, MakeZtfConfig<pool>(digits));
605 }
606 
607 template <uint8_t digits, DisassemblerZtf::ReferencePool pool>
MakeWriteAbs(MutableBufferView image)608 std::unique_ptr<ReferenceWriter> DisassemblerZtf::MakeWriteAbs(
609     MutableBufferView image) {
610   static_assert(digits >= 1 && digits <= kMaxDigitCount,
611                 "|digits| must be in range [1, 3]");
612   return std::make_unique<ZtfReferenceWriter<ztf::LineCol>>(
613       image, translator_, MakeZtfConfig<pool>(digits));
614 }
615 
616 template <uint8_t digits, DisassemblerZtf::ReferencePool pool>
MakeWriteRel(MutableBufferView image)617 std::unique_ptr<ReferenceWriter> DisassemblerZtf::MakeWriteRel(
618     MutableBufferView image) {
619   static_assert(digits >= 1 && digits <= kMaxDigitCount,
620                 "|digits| must be in range [1, 3]");
621   return std::make_unique<ZtfReferenceWriter<ztf::DeltaLineCol>>(
622       image, translator_, MakeZtfConfig<pool>(digits));
623 }
624 
Parse(ConstBufferView image)625 bool DisassemblerZtf::Parse(ConstBufferView image) {
626   image_ = image;
627   if (!ReadZtfHeader(image_))
628     return false;
629 
630   CHECK_GE(image_.size(),
631            static_cast<size_t>(kTotalMagicSize));  // Needs header and footer.
632 
633   // Find the terminating footer "txTZ\n" that indicates the end of the image.
634   offset_t offset = 0;
635   for (; offset <= image_.size() - kFooterMagicSize; offset++) {
636     if (image_.read<uint8_t>(offset) == 't' &&
637         image_.read<uint8_t>(offset + 1) == 'x' &&
638         image_.read<uint8_t>(offset + 2) == 'T' &&
639         image_.read<uint8_t>(offset + 3) == 'Z' &&
640         image_.read<uint8_t>(offset + 4) == '\n') {
641       break;
642     }
643   }
644 
645   // If no footer is found before the end of the image then the parsing failed.
646   if (offset > image_.size() - kFooterMagicSize)
647     return false;
648   image_.shrink(offset + kFooterMagicSize);
649 
650   return translator_.Init(image_);
651 }
652 
653 }  // namespace zucchini
654