cybersecurity · beginner · ~15 min

Build one line of an xxd-style hex dump

A reusable hex-dump primitive.

Challenge

Format one row of an xxd-style hex dump — the universal byte-level view used in forensics.

Task

Implement int hex_dump_line(unsigned long offset, const unsigned char *bytes, int n, char *out, int cap) that writes one formatted line into out.

Line format:

OFFSET: HH HH HH HH HH HH HH HH  HH HH HH HH HH HH HH HH  ASCII
  • OFFSET: 8 lowercase hex digits followed by : .
  • The 16 byte slots are shown in two groups of 8, with an extra space between the groups.
  • Present bytes render as HH (lowercase hex); missing slots (n < 16) render as 3 spaces.
  • ASCII gutter: each of the n bytes renders as itself if printable (>= 0x20 && < 0x7f), else ..

Input

  • offset: the row's starting offset.
  • bytes, n: the row data and its byte count (0 <= n <= 16). The grader passes fixed buffers.
  • out, cap: destination buffer and its size.

Output

Returns the number of bytes written (excluding the NUL terminator), or -1 if the line would not fit in cap.

Example

offset 0x10, 16 bytes "Hello!" + binary   ->   line containing "00000010:" and gutter "Hello!.."
offset 0, bytes {0x41,0x42,0x43}, n=3      ->   line containing "00000000:" and gutter "ABC"
cap too small (e.g. 20)                    ->   -1

Edge cases

  • n == 0: an empty row (offset, blank slots, no gutter characters).
  • n == 16: a full row.
  • Non-printable bytes show as . in the gutter.

Rules

  • Bound every write to cap; return -1 rather than overflow.

Why this matters

A hex dump is the universal byte-level debugger. Knowing how to lay one out in 16-byte rows with an ASCII gutter is a daily forensic skill.

Input format

An offset, a byte buffer bytes with count n (0..16), a destination out, and its size cap.

Output format

Bytes written excluding NUL, or -1 if the line would not fit in cap.

Constraints

Bound every write to cap; missing slots are 3 spaces; gutter prints 0x20..0x7e or ..

Starter code

#include <stddef.h>
int hex_dump_line(unsigned long offset, const unsigned char *bytes, int n, char *out, int cap) { /* TODO */ (void)offset; (void)bytes; (void)n; (void)out; (void)cap; return -1; }

Common mistakes

Treating non-ASCII as printable. Off-by-one on the row of 16.

Edge cases to handle

n == 0 (empty row). n == 16 (full row).

Complexity

O(n).

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.