xref: /aosp_15_r20/external/pigweed/pw_hex_dump/public/pw_hex_dump/hex_dump.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #pragma once
16 
17 #include <cstdint>
18 
19 #include "pw_bytes/span.h"
20 #include "pw_span/span.h"
21 #include "pw_status/status.h"
22 
23 namespace pw::dump {
24 
25 /// Size, in bytes, of the resulting string after converting an address to a
26 /// UTF-8 encoded hex representation. This constant depends on the size
27 /// a of a pointer.
28 ///
29 /// Example (32-bit):
30 ///   0x0000F00D
31 ///
32 /// Note: the +2 accounts for the "0x" prefix.
33 constexpr const size_t kHexAddrStringSize = sizeof(uintptr_t) * 2 + 2;
34 
35 /// The formatted hex dumper is a configurable class that can dump hex in
36 /// various formats. The default produced output is xxd compatible, though
37 /// there are options to further adjust the output. One example is address
38 /// prefixing, where base memory address of each line is used instead of an
39 /// offset.
40 ///
41 /// It is strongly recommended NOT to directly depend on this dump format;
42 /// pw_hex_dump does NOT guarantee stability for the output format, but strives
43 /// to remain xxd compatible.
44 ///
45 /// Default:
46 /// @code
47 ///   Offs.  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  Text
48 ///   0000: A4 CC 32 62 9B 46 38 1A 23 1A 2A 7A BC E2 40 A0  ..2b.F8.#.*z..@.
49 ///   0010: FF 33 E5 2B 9E 9F 6B 3C BE 9B 89 3C 7E 4A 7A 48  .3.+..k<...<~JzH
50 ///   0020: 18                                               .
51 /// @endcode
52 ///
53 /// Example 1:
54 /// `(32-bit machine, group_every=4,
55 /// prefix_mode=kAbsolute, bytes_per_line = 8)`
56 /// @code
57 ///   Address      0        4        Text
58 ///   0x20000000: A4CC3262 9B46381A  ..2b.F8.
59 ///   0x20000008: 231A2A7A BCE240A0  #.*z..@.
60 ///   0x20000010: FF33E52B 9E9F6B3C  .3.+..k<
61 ///   0x20000018: BE9B893C 7E4A7A48  ...<~JzH
62 ///   0x20000020: 18                 .
63 /// @endcode
64 ///
65 /// Example 2:
66 /// `(group_every=1, bytes_per_line = 16)`
67 /// @code
68 ///   Offs.  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
69 ///   0000: A4 CC 32 62 9B 46 38 1A 23 1A 2A 7A BC E2 40 A0
70 ///   0010: FF 33 E5 2B 9E 9F 6B 3C BE 9B 89 3C 7E 4A 7A 48
71 ///   0020: 18
72 /// @endcode
73 ///
74 /// Example 3:
75 /// `(group_every=0, prefix_mode=kNone, show_header=false, show_ascii=false)`
76 /// @code
77 ///   A4CC32629B46381A231A2A7ABCE240A0
78 ///   FF33E52B9E9F6B3CBE9B893C7E4A7A48
79 ///   18
80 /// @endcode
81 class FormattedHexDumper {
82  public:
83   enum AddressMode {
84     kDisabled = 0,
85     kOffset = 1,
86     kAbsolute = 2,
87   };
88 
89   struct Flags {
90     /// Sets the number of source data bytes to print in each formatted line.
91     uint8_t bytes_per_line : 8;
92 
93     /// Inserts a space every N bytes for readability. Note that this is in
94     /// number of bytes converted to characters. Set to zero to disable.
95     ///
96     /// i.e. a value of 2 results in:
97     /// @code
98     ///   0x00000000: 0102 0304 0506 0708
99     /// @endcode
100     uint8_t group_every : 8;
101 
102     /// Show or hide ascii interpretation of binary data.
103     bool show_ascii : 1;
104 
105     /// Show descriptive column headers.
106     bool show_header : 1;
107 
108     /// Prefix each line of the dump with an offset or absolute address.
109     AddressMode prefix_mode : 2;
110   };
111 
112   Flags flags = {.bytes_per_line = 16,
113                  .group_every = 1,
114                  .show_ascii = true,
115                  .show_header = true,
116                  .prefix_mode = AddressMode::kOffset};
117 
118   FormattedHexDumper() = default;
FormattedHexDumper(span<char> dest)119   FormattedHexDumper(span<char> dest) {
120     SetLineBuffer(dest)
121         .IgnoreError();  // TODO: b/242598609 - Handle Status properly
122   }
FormattedHexDumper(span<char> dest,Flags config_flags)123   FormattedHexDumper(span<char> dest, Flags config_flags)
124       : flags(config_flags) {
125     SetLineBuffer(dest)
126         .IgnoreError();  // TODO: b/242598609 - Handle Status properly
127   }
128 
129   // TODO: b/234892215 - Add iterator support.
130 
131   /// Set the destination buffer that the hex dumper will write to line-by-line.
132   ///
133   /// @return @rst
134   ///
135   /// .. pw-status-codes::
136   ///
137   ///   RESOURCE_EXHAUSTED: The buffer was set, but is too small to fit the
138   ///   current formatting configuration.
139   ///
140   ///   INVALID_ARGUMENT: The destination buffer is invalid (nullptr or zero-
141   ///   length).
142   ///
143   /// @endrst
144   Status SetLineBuffer(span<char> dest);
145 
146   /// Begin dumping the provided data. Does NOT populate the line buffer with
147   /// a string, simply resets the statefulness to track this buffer.
148   ///
149   /// @return @rst
150   ///
151   /// .. pw-status-codes::
152   ///
153   ///   OK: Ready to begin dump.
154   ///
155   ///   INVALID_ARGUMENT: The source data starts at null, but has been set.
156   ///
157   ///   FAILED_PRECONDITION: Line buffer too small to hold current formatting
158   ///   settings.
159   ///
160   /// @endrst
161   Status BeginDump(ConstByteSpan data);
162 
163   /// Dumps a single line to the line buffer.
164   ///
165   /// Example usage:
166   ///
167   /// @code{.cpp}
168   ///   std::array<char, 80> temp;
169   ///   FormattedHexDumper hex_dumper(temp);
170   ///   hex_dumper.BeginDump(my_data);
171   ///   while(hex_dumper.DumpLine().ok()) {
172   ///     LOG_INFO("%s", temp.data());
173   ///   }
174   /// @endcode
175   ///
176   /// @return @rst
177   ///
178   /// .. pw-status-codes::
179   ///
180   ///   OK:  A line has been written to the line buffer.
181   ///
182   ///   RESOURCE_EXHAUSTED:  All the data has been dumped.
183   ///
184   ///   FAILED_PRECONDITION:  Destination line buffer is too small to fit
185   ///   current formatting configuration.
186   ///
187   /// @endrst
188   Status DumpLine();
189 
190  private:
191   Status ValidateBufferSize();
192   Status PrintFormatHeader();
193 
194   size_t current_offset_;
195   span<char> dest_;
196   ConstByteSpan source_data_;
197 };
198 
199 /// Dumps a `uintptr_t` to a character buffer as a hex address. This may be
200 /// useful to print out an address in a generalized way when `%z` and `%p`
201 /// aren't supported by a standard library implementation. The destination
202 /// buffer MUST be large enough to hold `kHexAddrStringSize + 1 (null
203 /// terminator)` bytes.
204 ///
205 /// Example (64-bit):
206 /// @code
207 ///   0x000000000022b698
208 /// @endcode
209 ///
210 /// Example (32-bit):
211 /// @code
212 ///   0x70000000
213 /// @endcode
214 ///
215 /// @return @rst
216 ///
217 /// .. pw-status-codes::
218 ///
219 ///   OK: Address has been written to the buffer.
220 ///
221 ///   INVALID_ARGUMENT: The destination buffer is invalid (nullptr).
222 ///
223 ///   RESOURCE_EXHAUSTED: The destination buffer is too small. No data written.
224 ///
225 /// @endrst
226 Status DumpAddr(span<char> dest, uintptr_t addr);
DumpAddr(span<char> dest,const void * ptr)227 inline Status DumpAddr(span<char> dest, const void* ptr) {
228   uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
229   return DumpAddr(dest, addr);
230 }
231 
232 }  // namespace pw::dump
233