difftastic/vendor/tree-sitter-hare/example/hex.ha

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");
};