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}