1#include <limits.h>
2//#include <stdbit.h> not in libc yet, using __builtin_* functions
3#include <stddef.h>
4#include <string.h>
5
6#include "dump.h"
7
8static inline unsigned first_leading_one(size_t value)
9{
10 static_assert(sizeof(size_t) == sizeof(unsigned long));
11 //return stdc_first_leading_one(value);
12 return ULONG_WIDTH - __builtin_clzl(value) - 1;
13}
14
15static void print_indent(FILE* fp, int indent)
16{
17 for (int i = 0; i < indent; i++ ) {
18 fputc(' ', fp);
19 }
20}
21
22static char hexdigits[] = "0123456789ABCDEF";
23
24static void print_addr(FILE* fp, uint8_t* addr, unsigned addr_width)
25{
26 unsigned shift = addr_width << 2;
27 for (unsigned i = 0; i < addr_width; i++) {
28 shift -= 4;
29 fputc(hexdigits[(((ptrdiff_t) addr) >> shift) & 15], fp);
30 }
31 fputc(':', fp);
32 fputc(' ', fp);
33}
34
35static void print_hex(FILE* fp, uint8_t data)
36{
37 fputc(hexdigits[data >> 4], fp);
38 fputc(hexdigits[data & 15], fp);
39}
40
41static void print_row(FILE* fp, uint8_t* addr, uint8_t* display_addr, unsigned addr_width, bool with_chars)
42{
43 print_addr(fp, display_addr, addr_width);
44 for(unsigned i = 0; i < 16; i++) {
45 if (i == 8) {
46 fputs("- ", fp);
47 }
48 print_hex(fp, addr[i]);
49 fputc(' ', fp);
50 }
51 if (with_chars) {
52 for(unsigned i = 0; i < 16; i++) {
53 uint8_t c = addr[i];
54 if (c < 32 || c > 127) {
55 c = '.';
56 }
57 fputc(c, fp);
58 }
59 }
60 fputc('\n', fp);
61}
62
63static void print_same_rows(FILE* fp, unsigned indent, unsigned num_same_rows,
64 uint8_t* row, uint8_t* display_addr, unsigned addr_width, bool with_chars)
65{
66 if (num_same_rows > 3) {
67 print_indent(fp, indent);
68 fprintf(fp, "-- %u same rows --\n", num_same_rows - 1);
69 print_indent(fp, indent);
70 print_row(fp, row, display_addr - 16, addr_width, with_chars);
71 } else if (num_same_rows) {
72 do {
73 print_indent(fp, indent);
74 print_row(fp, row, display_addr - (16 * num_same_rows), addr_width, with_chars);
75 } while (num_same_rows--);
76 }
77}
78
79void dump_hex(FILE* fp, unsigned indent, uint8_t* addr, unsigned size, uint8_t* display_addr, bool aligned, bool with_chars)
80{
81//fprintf(fp, "DUMP: addr=%p, size=%u, display_addr=%p, ", addr, size, display_addr);
82 unsigned offset;
83 if (aligned) {
84 offset = (unsigned) (((size_t) addr) & 15);
85 addr -= offset;
86 display_addr -= offset;
87 size += offset;
88 } else {
89 offset = 0;
90 }
91//fprintf(fp, "offset=%u, remainder=%u, size=%u\n", offset, size & 15, size - (size & 15));
92 uint8_t* max_addr = display_addr + size;
93 unsigned addr_width = (first_leading_one((size_t) max_addr) + 3) >> 2;
94 if (addr_width < 4) {
95 addr_width = 4;
96 }
97
98 unsigned i = 0;
99
100 if (offset) {
101 // print row with blank leading and trailing bytes
102 print_indent(fp, indent);
103 print_addr(fp, display_addr, addr_width);
104 unsigned sz = (size < 16)? size : 16;
105 unsigned j = 0;
106 for(; j < offset; j++) {
107 if (j == 8) {
108 fputs(" ", fp);
109 }
110 fputs(" ", fp);
111 }
112 for(; j < sz; j++) {
113 if (j == 8) {
114 fputs("- ", fp);
115 }
116 print_hex(fp, addr[j]);
117 fputc(' ', fp);
118 }
119 for(; j < 16; j++) {
120 if (j == 8) {
121 fputs(" ", fp);
122 }
123 fputs(" ", fp);
124 }
125 if (with_chars) {
126 for(j = 0; j < offset; j++) {
127 fputc(' ', fp);
128 }
129 for(; j < sz; j++) {
130 uint8_t c = addr[j];
131 if (c < 32 || c > 127) {
132 c = '.';
133 }
134 fputc(c, fp);
135 }
136 }
137 fputc('\n', fp);
138 if (size < 16) {
139 return;
140 }
141 i += 16;
142 addr += 16;
143 display_addr += 16;
144 }
145
146 unsigned remainder = size & 15;
147 size -= remainder;
148
149 // print full rows
150 unsigned num_rows = 0;
151 unsigned num_same_rows = 0;
152 uint8_t prev_row[16];
153 while (i < size) {
154 if (num_rows) {
155 // coalesce duplicate rows
156 if (memcmp(addr, prev_row, 16) == 0) {
157 num_same_rows++;
158 goto _continue;
159 }
160 print_same_rows(fp, indent, num_same_rows, prev_row, display_addr, addr_width, with_chars);
161 num_same_rows = 0;
162 }
163 print_indent(fp, indent);
164 print_row(fp, addr, display_addr, addr_width, with_chars);
165 memcpy(prev_row, addr, 16);
166
167_continue:
168 i += 16;
169 addr += 16;
170 display_addr += 16;
171 num_rows++;
172 }
173 print_same_rows(fp, indent, num_same_rows, prev_row, display_addr, addr_width, with_chars);
174
175 // print last incomplete row
176 if (remainder) {
177 print_indent(fp, indent);
178 print_addr(fp, display_addr, addr_width);
179 unsigned j = 0;
180 for(; j < remainder; j++) {
181 if (j == 8) {
182 fputs("- ", fp);
183 }
184 print_hex(fp, addr[j]);
185 fputc(' ', fp);
186 }
187 for(; j < 16; j++) {
188 if (j == 8) {
189 fputs(" ", fp);
190 }
191 fputs(" ", fp);
192 }
193 if (with_chars) {
194 for(j = 0; j < remainder; j++) {
195 uint8_t c = addr[j];
196 if (c < 32 || c > 127) {
197 c = '.';
198 }
199 fputc(c, fp);
200 }
201 }
202 fputc('\n', fp);
203 }
204}
205
206void dump_hex_simple(FILE* fp, uint8_t* data, unsigned size)
207{
208 dump_hex(fp, 0, data, size, data, true, true);
209}