mirror of https://github.com/Wilfred/difftastic/
118 lines
3.1 KiB
Plaintext
118 lines
3.1 KiB
Plaintext
use ascii;
|
|
use bytes;
|
|
use fmt;
|
|
use io;
|
|
use strconv;
|
|
use strings;
|
|
use strio;
|
|
|
|
// Error returned when attempting to decode an invalid hex string.
|
|
export type invalid = void!;
|
|
|
|
// Encodes a byte slice as a hexadecimal string and writes it to a stream.
|
|
export fn encode(sink: *io::stream, b: []u8) (size | io::error) = {
|
|
let z = 0z;
|
|
for (let i = 0z; i < len(b); i += 1) {
|
|
let s = strconv::u8tosb(b[i], strconv::base::HEX_LOWER);
|
|
if (len(s) == 1) {
|
|
z += io::write(sink, ['0': u32: u8])?;
|
|
};
|
|
z += io::write(sink, strings::toutf8(s))?;
|
|
};
|
|
return z;
|
|
};
|
|
|
|
// Encodes a byte slice as a hexadecimal string and returns it. The caller must
|
|
// free the return value.
|
|
export fn encodestr(b: []u8) str = {
|
|
let sink = strio::dynamic();
|
|
encode(sink, b) as size;
|
|
return strio::finish(sink);
|
|
};
|
|
|
|
@test fn encode() void = {
|
|
let in: [_]u8 = [0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xF0, 0x0D];
|
|
let s = encodestr(in);
|
|
defer free(s);
|
|
assert(s == "cafebabedeadf00d");
|
|
};
|
|
|
|
// Decodes a string of hexadecimal bytes into a byte slice. The caller must free
|
|
// the return value.
|
|
export fn decode(s: str) ([]u8 | invalid) = {
|
|
if (len(s) % 2 != 0) {
|
|
return invalid;
|
|
};
|
|
let buf: []u8 = alloc([], len(s) / 2);
|
|
let s = strings::toutf8(s);
|
|
for (let i = 0z; i < len(s) / 2; i += 1) {
|
|
let oct = strings::fromutf8_unsafe(s[i * 2..i * 2 + 2]);
|
|
let u = match (strconv::stou8b(oct, 16)) {
|
|
(strconv::invalid | strconv::overflow) => return invalid,
|
|
u: u8 => u,
|
|
};
|
|
append(buf, u);
|
|
};
|
|
return buf;
|
|
};
|
|
|
|
@test fn decode() void = {
|
|
let s = decode("cafebabedeadf00d") as []u8;
|
|
defer free(s);
|
|
assert(bytes::equal(s, [0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xF0, 0x0D]));
|
|
|
|
decode("this is not hex") as invalid;
|
|
};
|
|
|
|
// Outputs a dump of hex data to a stream alongside the offset and an ASCII
|
|
// representation (if applicable).
|
|
//
|
|
// Example output:
|
|
//
|
|
// 00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
|
|
// 00000010 03 00 3e 00 01 00 00 00 80 70 01 00 00 00 00 00 |..>......p......|
|
|
export fn dump(out: *io::stream, data: []u8) (void | io::error) = {
|
|
let datalen = len(data): u32;
|
|
|
|
for (let off = 0u32; off < datalen; off += 16) {
|
|
fmt::fprintf(out, "{:08x} ", off)?;
|
|
|
|
let toff = 0z;
|
|
for (let i = 0z; i < 16 && off + i < datalen; i += 1) {
|
|
let val = data[off + i];
|
|
toff += fmt::fprintf(out, "{}{:02x} ",
|
|
if (i == 8) " " else "", val)?;
|
|
};
|
|
|
|
// Align ASCII representation, max width of hex part (48) +
|
|
// spacing around it
|
|
for (toff < 50; toff += 1) {
|
|
fmt::fprint(out, " ")?;
|
|
};
|
|
|
|
fmt::fprint(out, "|")?;
|
|
for (let i = 0z; i < 16 && off + i < datalen; i += 1) {
|
|
let r = data[off + i]: u32: rune;
|
|
|
|
fmt::fprint(out, if (ascii::isprint(r)) r else '.')?;
|
|
};
|
|
fmt::fprint(out, "|\n")?;
|
|
};
|
|
};
|
|
|
|
@test fn dump() void = {
|
|
let in: [_]u8 = [
|
|
0x7F, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0xCA, 0xFE,
|
|
0xBA, 0xBE, 0xDE, 0xAD, 0xF0, 0x0D, 0xCE, 0xFE, 0xBA, 0xBE,
|
|
0xDE, 0xAD, 0xF0, 0x0D
|
|
];
|
|
|
|
let sink = strio::dynamic();
|
|
dump(sink, in) as void;
|
|
|
|
let s = strio::finish(sink);
|
|
assert(s ==
|
|
"00000000 7f 45 4c 46 02 01 01 00 ca fe ba be de ad f0 0d |.ELF............|\n"
|
|
"00000010 ce fe ba be de ad f0 0d |........|\n");
|
|
};
|