Skip to content

Commit

Permalink
General refactor to have a better module API
Browse files Browse the repository at this point in the history
  • Loading branch information
Arnau478 committed May 12, 2024
1 parent 4e19db8 commit aee6fe7
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 85 deletions.
9 changes: 5 additions & 4 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,15 @@ fn getVersion(b: *std.Build) SemanticVersion {
}
}

fn addExe(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, release: bool, build_options: *std.Build.Step.Options) *std.Build.Step.Compile {
fn addExe(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, release: bool, build_options: *std.Build.Step.Options, hevi_mod: *std.Build.Module) *std.Build.Step.Compile {
const exe = b.addExecutable(.{
.name = if (release) b.fmt("hevi-{s}-{s}", .{ @tagName(target.result.cpu.arch), @tagName(target.result.os.tag) }) else "hevi",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});

exe.root_module.addImport("hevi", hevi_mod);
exe.root_module.addOptions("build_options", build_options);

return exe;
Expand All @@ -89,11 +90,11 @@ pub fn build(b: *std.Build) !void {
var build_options = b.addOptions();
build_options.addOption(SemanticVersion, "version", getVersion(b));

_ = b.addModule("hevi", .{
const mod = b.addModule("hevi", .{
.root_source_file = .{ .path = "src/hevi.zig" },
});

const exe = addExe(b, target, optimize, false, build_options);
const exe = addExe(b, target, optimize, false, build_options, mod);
b.installArtifact(exe);

const docs_step = b.step("docs", "Build the documentation");
Expand All @@ -115,7 +116,7 @@ pub fn build(b: *std.Build) !void {

const release_step = b.step("release", "Create release builds for all targets");
for (release_targets) |rt| {
const rexe = addExe(b, b.resolveTargetQuery(rt), .ReleaseSmall, true, build_options);
const rexe = addExe(b, b.resolveTargetQuery(rt), .ReleaseSmall, true, build_options, mod);
release_step.dependOn(&b.addInstallArtifact(rexe, .{ .dest_sub_path = try std.fs.path.join(b.allocator, &.{ "release", rexe.name }) }).step);
}

Expand Down
9 changes: 8 additions & 1 deletion src/DisplayOptions.zig
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
const std = @import("std");
const DisplayOptions = @This();

/// Whether to use color or not
color: bool,
/// If true, uses uppercase; otherwise lowercase
uppercase: bool,
/// Print the size of the file at the end
show_size: bool,
/// Show a column with the offset into the file
show_offset: bool,
/// Show a column with the ASCII interpretation
show_ascii: bool,
/// Skip lines if they're the same as the one before and after it
skip_lines: bool,
parser: ?OptionString,
/// Override the binary parser that is used
parser: ?OptionString = null,

pub const OptionString = struct {
is_allocated: bool = false,
Expand Down
22 changes: 4 additions & 18 deletions src/argparse.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const build_options = @import("build_options");
const std = @import("std");

const parsers = @import("parser.zig").parsers;
const build_options = @import("build_options");
const hevi = @import("hevi");

pub const ParseResult = struct {
filename: []const u8,
Expand Down Expand Up @@ -39,19 +38,6 @@ fn printError(comptime fmt: []const u8, args: anytype) noreturn {
}

fn printHelp() noreturn {
const parser_list: [parsers.len][]const u8 = comptime v: {
var list: [parsers.len][]const u8 = undefined;
var pos: usize = 0;
for (parsers) |parser| {
var split = std.mem.splitAny(u8, @typeName(parser), ".");
_ = split.first();

list[pos] = split.next().?;
pos += 1;
}
break :v list;
};

std.debug.print(
\\hevi - hex viewer
\\
Expand All @@ -71,12 +57,12 @@ fn printHelp() noreturn {
\\
, .{});

for (parser_list) |parser| {
for (std.enums.values(hevi.Parser)) |parser| {
// How many tabs (4 spaces) we want to print
for (0..9) |_| {
std.debug.print(" ", .{});
}
std.debug.print("- {s}\n", .{parser});
std.debug.print("- {s}\n", .{@tagName(parser)});
}

std.debug.print(
Expand Down
61 changes: 57 additions & 4 deletions src/hevi.zig
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const std = @import("std");
const NormalizedSize = @import("NormalizedSize.zig");
const parser = @import("parser.zig");

pub const DisplayOptions = @import("DisplayOptions.zig");

/// ANSI color
pub const TextColor = struct {
base: Base,
dim: bool,
Expand Down Expand Up @@ -51,6 +51,7 @@ pub const TextColor = struct {
}
};

/// Generalized color, agnostic to the current color palette
pub const PaletteColor = enum {
normal,
normal_alt,
Expand All @@ -66,6 +67,7 @@ pub const PaletteColor = enum {
c5_alt,
};

/// A color palette, that associates `PaletteColor`s to `TextColor`s
pub const ColorPalette = std.enums.EnumFieldStruct(PaletteColor, TextColor, null);

const palette: ColorPalette = .{
Expand All @@ -83,6 +85,58 @@ const palette: ColorPalette = .{
.c5_alt = .{ .base = .cyan, .dim = true },
};

pub const Parser = enum {
elf,
pe,
data,

fn Namespace(self: Parser) type {
return switch (self) {
.elf => @import("parsers/elf.zig"),
.pe => @import("parsers/pe.zig"),
.data => @import("parsers/data.zig"),
};
}

pub fn matches(self: Parser, data: []const u8) bool {
return switch (self) {
inline else => |p| p.Namespace().matches(data),
};
}

pub fn getColors(self: Parser, colors: []PaletteColor, data: []const u8) void {
switch (self) {
inline else => |p| p.Namespace().getColors(colors, data),
}
}
};

fn getColors(allocator: std.mem.Allocator, reader: std.io.AnyReader, options: DisplayOptions) ![]const PaletteColor {
const data = try reader.readAllAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(data);

const colors = try allocator.alloc(PaletteColor, data.len);

inline for (comptime std.enums.values(Parser)) |parser| {
if (options.parser) |p| {
if (std.mem.eql(u8, @tagName(parser), p.string)) {
if (parser.matches(data)) {
parser.getColors(colors, data);
return colors;
} else {
std.debug.print("Error: the specified parser doesn't match the file format!\n", .{});
std.process.exit(1);
}
}
} else if (parser.matches(data)) {
parser.getColors(colors, data);
return colors;
}
}

@panic("No parser matched");
}

inline fn isPrintable(c: u8) bool {
return switch (c) {
0x20...0x7E => true,
Expand Down Expand Up @@ -233,10 +287,11 @@ fn display(reader: std.io.AnyReader, colors: []const TextColor, writer: std.io.A
}
}

/// Dump `data` to `writer`
pub fn dump(allocator: std.mem.Allocator, data: []const u8, writer: std.io.AnyWriter, options: DisplayOptions) !void {
var fbs = std.io.fixedBufferStream(data);

const colors = try parser.getColors(allocator, fbs.reader().any(), options);
const colors = try getColors(allocator, fbs.reader().any(), options);
defer allocator.free(colors);

fbs.reset();
Expand Down Expand Up @@ -281,7 +336,6 @@ test "basic dump" {
.show_ascii = false,
.skip_lines = false,
.show_offset = false,
.parser = null,
},
);
}
Expand All @@ -297,7 +351,6 @@ test "empty dump" {
.show_ascii = false,
.skip_lines = false,
.show_offset = false,
.parser = null,
},
);
}
13 changes: 6 additions & 7 deletions src/options.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const builtin = @import("builtin");
const std = @import("std");
const hevi = @import("hevi");
const argparse = @import("argparse.zig");
const DisplayOptions = @import("DisplayOptions.zig");

fn openConfigFile(allocator: std.mem.Allocator, env_map: std.process.EnvMap) ?std.meta.Tuple(&.{ std.fs.File, []const u8 }) {
const path: ?[]const u8 = switch (builtin.os.tag) {
Expand All @@ -24,19 +24,18 @@ fn openConfigFile(allocator: std.mem.Allocator, env_map: std.process.EnvMap) ?st
}, path orelse return null };
}

pub fn getOptions(allocator: std.mem.Allocator, args: argparse.ParseResult, stdout: std.fs.File) !DisplayOptions {
pub fn getOptions(allocator: std.mem.Allocator, args: argparse.ParseResult, stdout: std.fs.File) !hevi.DisplayOptions {
var envs = try std.process.getEnvMap(allocator);
defer envs.deinit();

// Default values
var options = DisplayOptions{
var options = hevi.DisplayOptions{
.color = stdout.supportsAnsiEscapeCodes(),
.uppercase = false,
.show_size = true,
.show_offset = true,
.show_ascii = true,
.skip_lines = true,
.parser = null,
};

// Config file
Expand Down Expand Up @@ -71,7 +70,7 @@ pub fn getOptions(allocator: std.mem.Allocator, args: argparse.ParseResult, stdo
return error.InvalidConfig;
};

inline for (std.meta.fields(DisplayOptions)) |opt_field| {
inline for (std.meta.fields(hevi.DisplayOptions)) |opt_field| {
if (std.mem.eql(u8, name, opt_field.name)) {
@field(options, opt_field.name) = switch (opt_field.type) {
bool => if (std.mem.eql(u8, value, "false"))
Expand All @@ -82,7 +81,7 @@ pub fn getOptions(allocator: std.mem.Allocator, args: argparse.ParseResult, stdo
try stderr.writer().print("Error: expected a bool for field {s} in config file\n", .{name});
return error.InvalidConfig;
},
DisplayOptions.OptionString, ?DisplayOptions.OptionString => .{ .is_allocated = true, .string = std.mem.trim(u8, try allocator.dupe(u8, value), "\"") },
hevi.DisplayOptions.OptionString, ?hevi.DisplayOptions.OptionString => .{ .is_allocated = true, .string = std.mem.trim(u8, try allocator.dupe(u8, value), "\"") },
else => {
try stderr.writer().print("Error: expected a {s} for field {s} in config file\n", .{ @typeName(opt_field.type), name });
return error.InvalidConfig;
Expand All @@ -105,7 +104,7 @@ pub fn getOptions(allocator: std.mem.Allocator, args: argparse.ParseResult, stdo
if (args.show_offset) |show_offset| options.show_offset = show_offset;
if (args.show_ascii) |show_ascii| options.show_ascii = show_ascii;
if (args.skip_lines) |skip_lines| options.skip_lines = skip_lines;
if (args.parser) |parser| DisplayOptions.OptionString.safeSet(allocator, &options, parser);
if (args.parser) |parser| hevi.DisplayOptions.OptionString.safeSet(allocator, &options, parser);

return options;
}
43 changes: 0 additions & 43 deletions src/parser.zig

This file was deleted.

4 changes: 2 additions & 2 deletions src/parsers/data.zig
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const std = @import("std");
const PaletteColor = @import("../main.zig").PaletteColor;
const hevi = @import("../hevi.zig");

pub fn matches(_: []const u8) bool {
return true;
}

pub fn getColors(colors: []PaletteColor, data: []const u8) void {
pub fn getColors(colors: []hevi.PaletteColor, data: []const u8) void {
for (data, colors) |byte, *color| {
color.* = switch (byte) {
0x20...0x7E => .normal,
Expand Down
6 changes: 3 additions & 3 deletions src/parsers/elf.zig
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
const std = @import("std");
const PaletteColor = @import("../main.zig").PaletteColor;
const hevi = @import("../hevi.zig");

pub fn matches(data: []const u8) bool {
return std.mem.startsWith(u8, data, std.elf.MAGIC);
}

fn setRange(colors: []PaletteColor, offset: usize, len: usize, color: PaletteColor) void {
fn setRange(colors: []hevi.PaletteColor, offset: usize, len: usize, color: hevi.PaletteColor) void {
@memset(colors[offset .. offset + len], color);
}

pub fn getColors(colors: []PaletteColor, data: []const u8) void {
pub fn getColors(colors: []hevi.PaletteColor, data: []const u8) void {
@memset(colors, .normal_alt);

var fbs = std.io.fixedBufferStream(data);
Expand Down
6 changes: 3 additions & 3 deletions src/parsers/pe.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const PaletteColor = @import("../main.zig").PaletteColor;
const hevi = @import("../hevi.zig");

const DosHeader = packed struct(u512) {
magic: u16,
Expand Down Expand Up @@ -32,11 +32,11 @@ pub fn matches(data: []const u8) bool {
return std.mem.startsWith(u8, data, "MZ");
}

fn setRange(colors: []PaletteColor, offset: usize, len: usize, color: PaletteColor) void {
fn setRange(colors: []hevi.PaletteColor, offset: usize, len: usize, color: hevi.PaletteColor) void {
@memset(colors[offset .. offset + len], color);
}

pub fn getColors(colors: []PaletteColor, data: []const u8) void {
pub fn getColors(colors: []hevi.PaletteColor, data: []const u8) void {
@memset(colors, .normal_alt);

var fbs = std.io.fixedBufferStream(data);
Expand Down

0 comments on commit aee6fe7

Please sign in to comment.