Something went wrong. Try again.
Completely reimplimented in zig (eventually)
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177const std = @import("std");const Allocator = std.mem.Allocator;
pub const Segment = struct { base: u32, data: []u8,};
pub const Image = struct { arena: std.heap.ArenaAllocator, segments: []Segment, entry_point: ?u32,
pub fn deinit(self: *Image) void { self.arena.deinit(); }
pub fn totalBytes(self: *const Image) u64 { var total: u64 = 0; for (self.segments) |seg| { total += seg.data.len; } return total; }
pub fn findSegment(self: *const Image, address: u32) ?*const Segment { for (self.segments) |*seg| { if (address >= seg.base and address < seg.base + seg.data.len) return seg; } return null; }};
pub const ParseError = error{ MissingColon, TruncatedRecord, BadChecksum, UnsupportedRecordType, MissingEof, OutOfMemory,};
fn hexNibble(c: u8) ?u8 { return switch (c) { '0'...'9' => c - '0', 'A'...'F' => c - 'A' + 10, 'a'...'f' => c - 'a' + 10, else => null, };}
fn hexPair(s: []const u8) !u8 { const hi = hexNibble(s[0]) orelse return error.BadChecksum; const lo = hexNibble(s[1]) orelse return error.BadChecksum; return (hi << 4) | lo;}
fn appendData(a: Allocator, segments: *std.ArrayList(Segment), base: u32, data: []const u8) !void { if (data.len == 0) return; if (segments.items.len > 0) { const last = &segments.items[segments.items.len - 1]; const last_end: u64 = @as(u64, last.base) + last.data.len; if (last_end == base) { const old_len = last.data.len; last.data = try a.realloc(last.data, old_len + data.len); @memcpy(last.data[old_len..], data); return; } } try segments.append(a, .{ .base = base, .data = try a.dupe(u8, data) });}
pub fn parse(gpa: Allocator, text: []const u8) ParseError!Image { var arena = std.heap.ArenaAllocator.init(gpa); errdefer arena.deinit(); const a = arena.allocator();
var segments: std.ArrayList(Segment) = .empty; var upper: u32 = 0; var entry_point: ?u32 = null; var saw_eof = false;
var lines = std.mem.splitScalar(u8, text, '\n'); while (lines.next()) |raw_line| { if (saw_eof) break; const line = std.mem.trim(u8, raw_line, " \t\r"); if (line.len == 0) continue; if (line[0] != ':') return error.MissingColon; if (line.len < 11 or (line.len - 1) % 2 != 0) return error.TruncatedRecord;
var sum: u16 = 0; const body_len = line.len - 1; var record: [300]u8 = undefined; if (body_len / 2 > record.len) return error.TruncatedRecord; var i: usize = 0; while (i < body_len) : (i += 2) { record[i / 2] = try hexPair(line[1 + i .. 3 + i]); } const rec = record[0 .. body_len / 2]; for (rec) |byte| sum += byte; if (sum & 0xFF != 0) return error.BadChecksum;
const count: usize = rec[0]; if (rec.len != count + 5) return error.TruncatedRecord; const addr: u16 = (@as(u16, rec[1]) << 8) | rec[2]; const rtype: u8 = rec[3]; const payload = rec[4 .. 4 + count];
switch (rtype) { 0 => { const base = upper +% addr; try appendData(a, &segments, base, payload); }, 1 => saw_eof = true, 2 => upper = (@as(u32, (@as(u16, payload[0]) << 8) | payload[1])) << 4, 3 => {}, 4 => upper = (@as(u32, (@as(u16, payload[0]) << 8) | payload[1])) << 16, 5 => { if (count >= 4) { entry_point = (@as(u32, payload[0]) << 24) | (@as(u32, payload[1]) << 16) | (@as(u32, payload[2]) << 8) | payload[3]; } }, else => return error.UnsupportedRecordType, } }
if (!saw_eof) return error.MissingEof;
return .{ .arena = arena, .segments = try segments.toOwnedSlice(a), .entry_point = entry_point, };}
test "parse basic image with merge and entry point" { const text = ":0A000000112233445566778899AA4F\n" ++ ":0A000A00BBCCDDEEFF0011223344F1\n" ++ ":020000040001F9\n" ++ ":08000000DEADBEEFCAFEBABE80\n" ++ ":0400000500010000F6\n" ++ ":00000001FF\n";
var img = try parse(std.testing.allocator, text); defer img.deinit();
try std.testing.expectEqual(@as(usize, 2), img.segments.len); try std.testing.expectEqual(@as(u32, 0x0000), img.segments[0].base); try std.testing.expectEqual(@as(usize, 20), img.segments[0].data.len); try std.testing.expectEqual(@as(u8, 0x11), img.segments[0].data[0]); try std.testing.expectEqual(@as(u8, 0xAA), img.segments[0].data[9]); try std.testing.expectEqual(@as(u8, 0xBB), img.segments[0].data[10]); try std.testing.expectEqual(@as(u8, 0x44), img.segments[0].data[19]);
try std.testing.expectEqual(@as(u32, 0x00010000), img.segments[1].base); try std.testing.expectEqual(@as(usize, 8), img.segments[1].data.len); try std.testing.expectEqual(@as(u64, 28), img.totalBytes()); try std.testing.expectEqual(@as(?u32, 0x00010000), img.entry_point);
const seg = img.findSegment(0x00010004).?; try std.testing.expectEqual(@as(u8, 0xCA), seg.data[4]); try std.testing.expect(img.findSegment(0x12345678) == null);}
test "parse rejects bad checksum and garbage" { try std.testing.expectError( error.BadChecksum, parse(std.testing.allocator, ":0A000000112233445566778899AAFF\n:00000001FF\n"), ); try std.testing.expectError( error.MissingColon, parse(std.testing.allocator, "not hex at all\n"), );}