xref: /aosp_15_r20/external/pigweed/pw_hex_dump/hex_dump.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_hex_dump/hex_dump.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <cctype>
18*61c4878aSAndroid Build Coastguard Worker #include <cstddef>
19*61c4878aSAndroid Build Coastguard Worker #include <string_view>
20*61c4878aSAndroid Build Coastguard Worker 
21*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status_with_size.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_string/string_builder.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_string/type_to_string.h"
24*61c4878aSAndroid Build Coastguard Worker 
25*61c4878aSAndroid Build Coastguard Worker using pw::string::HexDigitCount;
26*61c4878aSAndroid Build Coastguard Worker using pw::string::IntToHexString;
27*61c4878aSAndroid Build Coastguard Worker 
28*61c4878aSAndroid Build Coastguard Worker namespace pw::dump {
29*61c4878aSAndroid Build Coastguard Worker namespace {
30*61c4878aSAndroid Build Coastguard Worker 
31*61c4878aSAndroid Build Coastguard Worker constexpr const std::string_view kAddressSeparator(": ");
32*61c4878aSAndroid Build Coastguard Worker constexpr const std::string_view kSectionSeparator("  ");
33*61c4878aSAndroid Build Coastguard Worker constexpr const std::string_view kAddressHeader("Address");
34*61c4878aSAndroid Build Coastguard Worker constexpr const std::string_view kOffsetHeader("Offs.");
35*61c4878aSAndroid Build Coastguard Worker constexpr const std::string_view kAsciiHeader("Text");
36*61c4878aSAndroid Build Coastguard Worker 
37*61c4878aSAndroid Build Coastguard Worker // Minimum number of hex characters to use when displaying dump offset.
38*61c4878aSAndroid Build Coastguard Worker constexpr const size_t kMinOffsetChars = 4;
39*61c4878aSAndroid Build Coastguard Worker 
PrintableChar(std::byte b)40*61c4878aSAndroid Build Coastguard Worker char PrintableChar(std::byte b) {
41*61c4878aSAndroid Build Coastguard Worker   if (std::isprint(std::to_integer<char>(b)) == 0) {
42*61c4878aSAndroid Build Coastguard Worker     return '.';
43*61c4878aSAndroid Build Coastguard Worker   }
44*61c4878aSAndroid Build Coastguard Worker   return std::to_integer<char>(b);
45*61c4878aSAndroid Build Coastguard Worker }
46*61c4878aSAndroid Build Coastguard Worker 
AddGroupingByte(size_t byte_index,FormattedHexDumper::Flags & flags,StringBuilder & builder)47*61c4878aSAndroid Build Coastguard Worker void AddGroupingByte(size_t byte_index,
48*61c4878aSAndroid Build Coastguard Worker                      FormattedHexDumper::Flags& flags,
49*61c4878aSAndroid Build Coastguard Worker                      StringBuilder& builder) {
50*61c4878aSAndroid Build Coastguard Worker   // Never add grouping when it is disabled.
51*61c4878aSAndroid Build Coastguard Worker   if (flags.group_every == 0) {
52*61c4878aSAndroid Build Coastguard Worker     return;
53*61c4878aSAndroid Build Coastguard Worker   }
54*61c4878aSAndroid Build Coastguard Worker   // If this byte isn't at the end of a group, don't add a space.
55*61c4878aSAndroid Build Coastguard Worker   if ((byte_index + 1) % flags.group_every != 0) {
56*61c4878aSAndroid Build Coastguard Worker     return;
57*61c4878aSAndroid Build Coastguard Worker   }
58*61c4878aSAndroid Build Coastguard Worker   // If this byte is the last byte in a line, don't add a grouping byte
59*61c4878aSAndroid Build Coastguard Worker   // (prevents trailing spaces).
60*61c4878aSAndroid Build Coastguard Worker   if (byte_index + 1 == flags.bytes_per_line) {
61*61c4878aSAndroid Build Coastguard Worker     return;
62*61c4878aSAndroid Build Coastguard Worker   }
63*61c4878aSAndroid Build Coastguard Worker 
64*61c4878aSAndroid Build Coastguard Worker   builder << ' ';
65*61c4878aSAndroid Build Coastguard Worker }
66*61c4878aSAndroid Build Coastguard Worker 
67*61c4878aSAndroid Build Coastguard Worker }  // namespace
68*61c4878aSAndroid Build Coastguard Worker 
DumpAddr(span<char> dest,uintptr_t addr)69*61c4878aSAndroid Build Coastguard Worker Status DumpAddr(span<char> dest, uintptr_t addr) {
70*61c4878aSAndroid Build Coastguard Worker   if (dest.data() == nullptr) {
71*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
72*61c4878aSAndroid Build Coastguard Worker   }
73*61c4878aSAndroid Build Coastguard Worker   // Include null terminator.
74*61c4878aSAndroid Build Coastguard Worker   if (dest.size() < kHexAddrStringSize + 1) {
75*61c4878aSAndroid Build Coastguard Worker     return Status::ResourceExhausted();
76*61c4878aSAndroid Build Coastguard Worker   }
77*61c4878aSAndroid Build Coastguard Worker   dest[0] = '0';
78*61c4878aSAndroid Build Coastguard Worker   dest[1] = 'x';
79*61c4878aSAndroid Build Coastguard Worker 
80*61c4878aSAndroid Build Coastguard Worker   return IntToHexString(addr, dest.subspan(2), sizeof(uintptr_t) * 2).status();
81*61c4878aSAndroid Build Coastguard Worker }
82*61c4878aSAndroid Build Coastguard Worker 
PrintFormatHeader()83*61c4878aSAndroid Build Coastguard Worker Status FormattedHexDumper::PrintFormatHeader() {
84*61c4878aSAndroid Build Coastguard Worker   StringBuilder builder(dest_);
85*61c4878aSAndroid Build Coastguard Worker 
86*61c4878aSAndroid Build Coastguard Worker   if (flags.prefix_mode != AddressMode::kDisabled) {
87*61c4878aSAndroid Build Coastguard Worker     std::string_view header(flags.prefix_mode == AddressMode::kOffset
88*61c4878aSAndroid Build Coastguard Worker                                 ? kOffsetHeader
89*61c4878aSAndroid Build Coastguard Worker                                 : kAddressHeader);
90*61c4878aSAndroid Build Coastguard Worker     // Pad to align to address width.
91*61c4878aSAndroid Build Coastguard Worker     size_t padding = 0;
92*61c4878aSAndroid Build Coastguard Worker     if (flags.prefix_mode == AddressMode::kOffset) {
93*61c4878aSAndroid Build Coastguard Worker       size_t offs_width =
94*61c4878aSAndroid Build Coastguard Worker           HexDigitCount(source_data_.size_bytes() + current_offset_);
95*61c4878aSAndroid Build Coastguard Worker       padding = std::max(offs_width, kMinOffsetChars);
96*61c4878aSAndroid Build Coastguard Worker     } else {
97*61c4878aSAndroid Build Coastguard Worker       padding = kHexAddrStringSize;
98*61c4878aSAndroid Build Coastguard Worker     }
99*61c4878aSAndroid Build Coastguard Worker 
100*61c4878aSAndroid Build Coastguard Worker     padding += kAddressSeparator.length();
101*61c4878aSAndroid Build Coastguard Worker     padding -= header.size();
102*61c4878aSAndroid Build Coastguard Worker 
103*61c4878aSAndroid Build Coastguard Worker     builder << header;
104*61c4878aSAndroid Build Coastguard Worker     builder.append(padding, ' ');
105*61c4878aSAndroid Build Coastguard Worker   }
106*61c4878aSAndroid Build Coastguard Worker 
107*61c4878aSAndroid Build Coastguard Worker   // Print offsets.
108*61c4878aSAndroid Build Coastguard Worker   for (size_t i = 0; i < static_cast<size_t>(flags.bytes_per_line); ++i) {
109*61c4878aSAndroid Build Coastguard Worker     // Early loop termination for when bytes_remaining <
110*61c4878aSAndroid Build Coastguard Worker     // bytes_per_line.
111*61c4878aSAndroid Build Coastguard Worker     if (flags.group_every != 0 &&
112*61c4878aSAndroid Build Coastguard Worker         i % static_cast<uint8_t>(flags.group_every) == 0) {
113*61c4878aSAndroid Build Coastguard Worker       builder << std::byte(i);
114*61c4878aSAndroid Build Coastguard Worker     } else {
115*61c4878aSAndroid Build Coastguard Worker       builder.append(2, ' ');
116*61c4878aSAndroid Build Coastguard Worker     }
117*61c4878aSAndroid Build Coastguard Worker     AddGroupingByte(i, flags, builder);
118*61c4878aSAndroid Build Coastguard Worker   }
119*61c4878aSAndroid Build Coastguard Worker 
120*61c4878aSAndroid Build Coastguard Worker   if (flags.show_ascii) {
121*61c4878aSAndroid Build Coastguard Worker     builder << kSectionSeparator;
122*61c4878aSAndroid Build Coastguard Worker     builder << kAsciiHeader;
123*61c4878aSAndroid Build Coastguard Worker   }
124*61c4878aSAndroid Build Coastguard Worker 
125*61c4878aSAndroid Build Coastguard Worker   return builder.status();
126*61c4878aSAndroid Build Coastguard Worker }
127*61c4878aSAndroid Build Coastguard Worker 
DumpLine()128*61c4878aSAndroid Build Coastguard Worker Status FormattedHexDumper::DumpLine() {
129*61c4878aSAndroid Build Coastguard Worker   if (source_data_.empty()) {
130*61c4878aSAndroid Build Coastguard Worker     return Status::ResourceExhausted();
131*61c4878aSAndroid Build Coastguard Worker   }
132*61c4878aSAndroid Build Coastguard Worker 
133*61c4878aSAndroid Build Coastguard Worker   if (!ValidateBufferSize().ok() || dest_.data() == nullptr) {
134*61c4878aSAndroid Build Coastguard Worker     return Status::FailedPrecondition();
135*61c4878aSAndroid Build Coastguard Worker   }
136*61c4878aSAndroid Build Coastguard Worker 
137*61c4878aSAndroid Build Coastguard Worker   if (dest_[0] == 0 && flags.show_header) {
138*61c4878aSAndroid Build Coastguard Worker     // First line, print out dump format header.
139*61c4878aSAndroid Build Coastguard Worker     return PrintFormatHeader();
140*61c4878aSAndroid Build Coastguard Worker   }
141*61c4878aSAndroid Build Coastguard Worker 
142*61c4878aSAndroid Build Coastguard Worker   StringBuilder builder(dest_);
143*61c4878aSAndroid Build Coastguard Worker   // Dump address/offset prefix.
144*61c4878aSAndroid Build Coastguard Worker   // TODO(amontanez): This block can be much nicer if StringBuilder exposed an
145*61c4878aSAndroid Build Coastguard Worker   // easy way to control zero padding for hex address.
146*61c4878aSAndroid Build Coastguard Worker   if (flags.prefix_mode != AddressMode::kDisabled) {
147*61c4878aSAndroid Build Coastguard Worker     uintptr_t val;
148*61c4878aSAndroid Build Coastguard Worker     size_t significant;
149*61c4878aSAndroid Build Coastguard Worker     if (flags.prefix_mode == AddressMode::kAbsolute) {
150*61c4878aSAndroid Build Coastguard Worker       val = reinterpret_cast<uintptr_t>(source_data_.data());
151*61c4878aSAndroid Build Coastguard Worker       builder << "0x";
152*61c4878aSAndroid Build Coastguard Worker       significant = HexDigitCount(val);
153*61c4878aSAndroid Build Coastguard Worker       builder.append(sizeof(uintptr_t) * 2 - significant, '0');
154*61c4878aSAndroid Build Coastguard Worker     } else {
155*61c4878aSAndroid Build Coastguard Worker       val = current_offset_;
156*61c4878aSAndroid Build Coastguard Worker       significant = HexDigitCount(val);
157*61c4878aSAndroid Build Coastguard Worker       if (significant < kMinOffsetChars) {
158*61c4878aSAndroid Build Coastguard Worker         builder.append(kMinOffsetChars - significant, '0');
159*61c4878aSAndroid Build Coastguard Worker       }
160*61c4878aSAndroid Build Coastguard Worker     }
161*61c4878aSAndroid Build Coastguard Worker     if (val != 0) {
162*61c4878aSAndroid Build Coastguard Worker       builder << reinterpret_cast<void*>(val);
163*61c4878aSAndroid Build Coastguard Worker     } else {
164*61c4878aSAndroid Build Coastguard Worker       builder.append(significant, '0');
165*61c4878aSAndroid Build Coastguard Worker     }
166*61c4878aSAndroid Build Coastguard Worker     builder << kAddressSeparator;
167*61c4878aSAndroid Build Coastguard Worker   }
168*61c4878aSAndroid Build Coastguard Worker 
169*61c4878aSAndroid Build Coastguard Worker   size_t bytes_in_line = std::min(source_data_.size_bytes(),
170*61c4878aSAndroid Build Coastguard Worker                                   static_cast<size_t>(flags.bytes_per_line));
171*61c4878aSAndroid Build Coastguard Worker   // Convert raw bytes to hex characters.
172*61c4878aSAndroid Build Coastguard Worker   for (size_t i = 0; i < bytes_in_line; ++i) {
173*61c4878aSAndroid Build Coastguard Worker     // Early loop termination for when bytes_remaining <
174*61c4878aSAndroid Build Coastguard Worker     // bytes_per_line.
175*61c4878aSAndroid Build Coastguard Worker     builder << source_data_[i];
176*61c4878aSAndroid Build Coastguard Worker     AddGroupingByte(i, flags, builder);
177*61c4878aSAndroid Build Coastguard Worker   }
178*61c4878aSAndroid Build Coastguard Worker   // Add padding spaces to ensure lines are aligned.
179*61c4878aSAndroid Build Coastguard Worker   if (flags.show_ascii) {
180*61c4878aSAndroid Build Coastguard Worker     for (size_t i = bytes_in_line;
181*61c4878aSAndroid Build Coastguard Worker          i < static_cast<size_t>(flags.bytes_per_line);
182*61c4878aSAndroid Build Coastguard Worker          ++i) {
183*61c4878aSAndroid Build Coastguard Worker       builder.append(2, ' ');
184*61c4878aSAndroid Build Coastguard Worker       AddGroupingByte(i, flags, builder);
185*61c4878aSAndroid Build Coastguard Worker     }
186*61c4878aSAndroid Build Coastguard Worker   }
187*61c4878aSAndroid Build Coastguard Worker 
188*61c4878aSAndroid Build Coastguard Worker   // Interpret bytes as characters.
189*61c4878aSAndroid Build Coastguard Worker   if (flags.show_ascii) {
190*61c4878aSAndroid Build Coastguard Worker     builder << kSectionSeparator;
191*61c4878aSAndroid Build Coastguard Worker     for (size_t i = 0; i < bytes_in_line; ++i) {
192*61c4878aSAndroid Build Coastguard Worker       builder << PrintableChar(source_data_[i]);
193*61c4878aSAndroid Build Coastguard Worker     }
194*61c4878aSAndroid Build Coastguard Worker   }
195*61c4878aSAndroid Build Coastguard Worker 
196*61c4878aSAndroid Build Coastguard Worker   source_data_ = source_data_.subspan(bytes_in_line);
197*61c4878aSAndroid Build Coastguard Worker   current_offset_ += bytes_in_line;
198*61c4878aSAndroid Build Coastguard Worker   return builder.status();
199*61c4878aSAndroid Build Coastguard Worker }
200*61c4878aSAndroid Build Coastguard Worker 
SetLineBuffer(span<char> dest)201*61c4878aSAndroid Build Coastguard Worker Status FormattedHexDumper::SetLineBuffer(span<char> dest) {
202*61c4878aSAndroid Build Coastguard Worker   if (dest.data() == nullptr || dest.size_bytes() == 0) {
203*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
204*61c4878aSAndroid Build Coastguard Worker   }
205*61c4878aSAndroid Build Coastguard Worker   dest_ = dest;
206*61c4878aSAndroid Build Coastguard Worker   return ValidateBufferSize().ok() ? OkStatus() : Status::ResourceExhausted();
207*61c4878aSAndroid Build Coastguard Worker }
208*61c4878aSAndroid Build Coastguard Worker 
BeginDump(ConstByteSpan data)209*61c4878aSAndroid Build Coastguard Worker Status FormattedHexDumper::BeginDump(ConstByteSpan data) {
210*61c4878aSAndroid Build Coastguard Worker   current_offset_ = 0;
211*61c4878aSAndroid Build Coastguard Worker   source_data_ = data;
212*61c4878aSAndroid Build Coastguard Worker   if (data.data() == nullptr) {
213*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
214*61c4878aSAndroid Build Coastguard Worker   }
215*61c4878aSAndroid Build Coastguard Worker   if (dest_.data() != nullptr && dest_.size_bytes() > 0) {
216*61c4878aSAndroid Build Coastguard Worker     dest_[0] = 0;
217*61c4878aSAndroid Build Coastguard Worker   }
218*61c4878aSAndroid Build Coastguard Worker   return ValidateBufferSize().ok() ? OkStatus() : Status::FailedPrecondition();
219*61c4878aSAndroid Build Coastguard Worker }
220*61c4878aSAndroid Build Coastguard Worker 
ValidateBufferSize()221*61c4878aSAndroid Build Coastguard Worker Status FormattedHexDumper::ValidateBufferSize() {
222*61c4878aSAndroid Build Coastguard Worker   // Minimum size is number of bytes per line as hex pairs plus the null
223*61c4878aSAndroid Build Coastguard Worker   // terminator.
224*61c4878aSAndroid Build Coastguard Worker   size_t required_size = flags.bytes_per_line * 2 + 1;
225*61c4878aSAndroid Build Coastguard Worker   if (flags.show_ascii) {
226*61c4878aSAndroid Build Coastguard Worker     required_size += kSectionSeparator.length() + flags.bytes_per_line;
227*61c4878aSAndroid Build Coastguard Worker   }
228*61c4878aSAndroid Build Coastguard Worker   if (flags.prefix_mode == AddressMode::kAbsolute) {
229*61c4878aSAndroid Build Coastguard Worker     required_size += kHexAddrStringSize;
230*61c4878aSAndroid Build Coastguard Worker     required_size += kAddressSeparator.length();
231*61c4878aSAndroid Build Coastguard Worker   } else if (flags.prefix_mode == AddressMode::kOffset) {
232*61c4878aSAndroid Build Coastguard Worker     required_size +=
233*61c4878aSAndroid Build Coastguard Worker         HexDigitCount(std::max(source_data_.size_bytes(), kMinOffsetChars));
234*61c4878aSAndroid Build Coastguard Worker     required_size += kAddressSeparator.length();
235*61c4878aSAndroid Build Coastguard Worker   }
236*61c4878aSAndroid Build Coastguard Worker   if (flags.group_every != 0) {
237*61c4878aSAndroid Build Coastguard Worker     required_size += (flags.bytes_per_line - 1) / flags.group_every;
238*61c4878aSAndroid Build Coastguard Worker   }
239*61c4878aSAndroid Build Coastguard Worker 
240*61c4878aSAndroid Build Coastguard Worker   if (dest_.size_bytes() < required_size) {
241*61c4878aSAndroid Build Coastguard Worker     return Status::ResourceExhausted();
242*61c4878aSAndroid Build Coastguard Worker   }
243*61c4878aSAndroid Build Coastguard Worker 
244*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
245*61c4878aSAndroid Build Coastguard Worker }
246*61c4878aSAndroid Build Coastguard Worker 
247*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::dump
248