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