pebble/src/libutil/hexdump.c

91 lines
2.8 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "util/hexdump.h"
#include <stdio.h>
#include <string.h>
// offset + gap + 8 hex bytes + gap + 8 hex bytes + gap + 8 ascii bytes
// + mini gap + 8 ascii bytes + null:
#define LINE_BUFFER_LENGTH (4 + 2 + (3 * 8) + 2 + (3 * 8) + 2 + 8 + 1 + 8 + 1)
void hexdump(const char *src_filename, int src_line_number, int level,
const uint8_t *data, size_t length, HexdumpLineCallback write_line_cb) {
char line_buffer[LINE_BUFFER_LENGTH];
unsigned int offset = 0;
while (offset < length) {
unsigned int buffer_offset = 0;
// Print data line offset
snprintf(line_buffer, LINE_BUFFER_LENGTH, "%04x ", offset);
buffer_offset += 6;
// Print the hex bytes
const unsigned int num_line_bytes = ((length - offset) > 16) ? 16 : (length - offset);
for (unsigned int i = 0; i < num_line_bytes; ++i) {
if (i == 8) {
line_buffer[buffer_offset++] = ' ';
}
snprintf(line_buffer + buffer_offset, LINE_BUFFER_LENGTH - buffer_offset, "%02x ",
data[offset + i]);
buffer_offset += 3;
}
// Calculate and apply padding between the hex dump and the ascii dump.
unsigned int required_padding = 2;
if (num_line_bytes < 16) {
// If we're printing a partial line, pad out the rest so the
// ascii lines up.
required_padding += (16 - num_line_bytes) * 3;
if (num_line_bytes <= 8)
{
// Account for the gap between the 8 byte hex blocks.
required_padding += 1;
}
}
memset(line_buffer + buffer_offset, ' ', required_padding);
buffer_offset += required_padding;
// Print the ASCII bytes
for (unsigned int i = 0; i < num_line_bytes; ++i) {
if (i == 8)
{
line_buffer[buffer_offset++] = ' ';
}
char c = data[offset + i];
if (c < 32 || c > 126 || c == '`') // ` is used for log hash string delimiting
{
c = '.';
}
line_buffer[buffer_offset++] = c;
}
// No need to pad after a partial ascii line, since we don't line up
// anything after it.
// Null terminate and print.
line_buffer[buffer_offset] = 0;
write_line_cb(level, src_filename, src_line_number, line_buffer);
offset += 16;
}
}