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 #include <span>
19
20 #include "pw_bytes/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 // It is strongly recommended NOT to directly depend on this dump format;
36 // pw_hex_dump does NOT guarantee stability for the output format, but strives
37 // to remain xxd compatible.
38 //
39 // Default:
40 // Offs. 0 1 2 3 4 5 6 7 8 9 A B C D E F Text
41 // 0000: A4 CC 32 62 9B 46 38 1A 23 1A 2A 7A BC E2 40 A0 ..2b.F8.#.*z..@.
42 // 0010: FF 33 E5 2B 9E 9F 6B 3C BE 9B 89 3C 7E 4A 7A 48 .3.+..k<...<~JzH
43 // 0020: 18 .
44 //
45 // Example 1 (32-bit machine, group_every=4, prefix_mode=kAbsolute,
46 // bytes_per_line = 8):
47 // Address 0 4 Text
48 // 0x20000000: A4CC3262 9B46381A ..2b.F8.
49 // 0x20000008: 231A2A7A BCE240A0 #.*z..@.
50 // 0x20000010: FF33E52B 9E9F6B3C .3.+..k<
51 // 0x20000018: BE9B893C 7E4A7A48 ...<~JzH
52 // 0x20000020: 18 .
53 //
54 // Example 2 (group_every=1, bytes_per_line = 16):
55 // Offs. 0 1 2 3 4 5 6 7 8 9 A B C D E F
56 // 0000: A4 CC 32 62 9B 46 38 1A 23 1A 2A 7A BC E2 40 A0
57 // 0010: FF 33 E5 2B 9E 9F 6B 3C BE 9B 89 3C 7E 4A 7A 48
58 // 0020: 18
59 //
60 // Example 3 (group_every=0, prefix_mode=kNone, show_header=false,
61 // show_ascii=false):
62 // A4CC32629B46381A231A2A7ABCE240A0
63 // FF33E52B9E9F6B3CBE9B893C7E4A7A48
64 // 18
65 class FormattedHexDumper {
66 public:
67 enum AddressMode {
68 kDisabled = 0,
69 kOffset = 1,
70 kAbsolute = 2,
71 };
72
73 struct Flags {
74 // Sets the number of source data bytes to print in each formatted line.
75 uint8_t bytes_per_line : 8;
76
77 // Inserts a space every N bytes for readability. Note that this is in
78 // number of bytes converted to characters. Set to zero to disable. I.e. a
79 // value of 2 results in:
80 // 0x00000000: 0102 0304 0506 0708
81 uint8_t group_every : 8;
82
83 // Show or hide ascii interpretation of binary data.
84 bool show_ascii : 1;
85
86 // Show descriptive column headers.
87 bool show_header : 1;
88
89 // Prefix each line of the dump with an offset or absolute address.
90 AddressMode prefix_mode : 2;
91 };
92
93 Flags flags = {.bytes_per_line = 16,
94 .group_every = 1,
95 .show_ascii = true,
96 .show_header = true,
97 .prefix_mode = AddressMode::kOffset};
98
99 FormattedHexDumper() = default;
FormattedHexDumper(std::span<char> dest)100 FormattedHexDumper(std::span<char> dest) { SetLineBuffer(dest); }
FormattedHexDumper(std::span<char> dest,Flags config_flags)101 FormattedHexDumper(std::span<char> dest, Flags config_flags)
102 : flags(config_flags) {
103 SetLineBuffer(dest);
104 }
105
106 // TODO(pwbug/218): Add iterator support.
107
108 // Set the destination buffer that the hex dumper will write to line-by-line.
109 //
110 // Returns:
111 // RESOURCE_EXHAUSTED - The buffer was set, but is too small to fit the
112 // current formatting configuration.
113 // INVALID_ARGUMENT - The destination buffer is invalid (nullptr or zero-
114 // length).
115 Status SetLineBuffer(std::span<char> dest);
116
117 // Begin dumping the provided data. Does NOT populate the line buffer with
118 // a string, simply resets the statefulness to track this buffer.
119 //
120 // Returns:
121 // OK - Ready to begin dump.
122 // INVALID_ARGUMENT - The source data starts at null, but has been set.
123 // FAILED_PRECONDITION - Line buffer too small to hold current formatting
124 // settings.
125 Status BeginDump(ConstByteSpan data);
126
127 // Dumps a single line to the line buffer.
128 //
129 // Example usage:
130 //
131 // std::array<char, 80> temp;
132 // FormattedHexDumper hex_dumper(temp);
133 // hex_dumper.BeginDump(my_data);
134 // while(hex_dumper.DumpLine().ok()) {
135 // LOG_INFO("%s", temp.data());
136 // }
137 //
138 // Returns:
139 // OK - A line has been written to the line buffer.
140 // RESOURCE_EXHAUSTED - All the data has been dumped.
141 // FAILED_PRECONDITION - Destination line buffer is too small to fit current
142 // formatting configuration.
143 Status DumpLine();
144
145 private:
146 Status ValidateBufferSize();
147 Status PrintFormatHeader();
148
149 size_t current_offset_;
150 std::span<char> dest_;
151 ConstByteSpan source_data_;
152 };
153
154 // Dumps a uintptr_t to a character buffer as a hex address. This may be useful
155 // to print out an address in a generalized way when %z and %p aren't supported
156 // by a standard library implementation. The destination buffer MUST be large
157 // enough to hold kHexAddrStringSize + 1 (null terminator) bytes.
158 //
159 // Example (64-bit):
160 // 0x000000000022b698
161 // Example (32-bit):
162 // 0x70000000
163 //
164 // Returns:
165 // OK - Address has been written to the buffer.
166 // INVALID_ARGUMENT - The destination buffer is invalid (nullptr).
167 // RESOURCE_EXHAUSTED - The destination buffer is too small. No data written.
168 Status DumpAddr(std::span<char> dest, uintptr_t addr);
DumpAddr(std::span<char> dest,const void * ptr)169 inline Status DumpAddr(std::span<char> dest, const void* ptr) {
170 uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
171 return DumpAddr(dest, addr);
172 }
173
174 } // namespace pw::dump
175