5 Commits

Author SHA1 Message Date
65d157852a feat: some optimizations 2025-10-05 21:13:54 +02:00
Tilo
d17540cb33 Feature/zig 0.15 upgrade (#1)
* fix: adjust build and stdout

* fix: compiles on 0.15

* fix: fix io changes

* fix: update github action zig version
2025-10-05 18:40:15 +02:00
0b60592e61 fix: fix memory leaks 2025-06-26 21:18:53 +02:00
b14907b3cc feat: ignore ds store 2025-06-25 11:53:00 +02:00
12fe0287da feat: fix macos support 2025-06-25 11:52:17 +02:00
6 changed files with 106 additions and 61 deletions

View File

@@ -51,7 +51,7 @@ jobs:
- name: Install Zig - name: Install Zig
uses: mlugg/setup-zig@v2 uses: mlugg/setup-zig@v2
with: with:
version: 0.14.0 version: 0.15.0
- name: Build for ${{ matrix.triple }} - name: Build for ${{ matrix.triple }}
run: | run: |

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.zig-cache .zig-cache
zig-out zig-out
.DS_Store

View File

@@ -15,8 +15,7 @@ pub fn build(b: *std.Build) void {
// set a preferred release mode, allowing the user to decide how to optimize. // set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const lib = b.addStaticLibrary(.{ const lib = b.addModule("csvu", .{
.name = "csvu",
// In this case the main source file is merely a path, however, in more // In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file. // complicated build scripts, this could be a generated file.
.root_source_file = b.path("src/root.zig"), .root_source_file = b.path("src/root.zig"),
@@ -27,13 +26,11 @@ pub fn build(b: *std.Build) void {
// This declares intent for the library to be installed into the standard // This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when // location when the user invokes the "install" step (the default step when
// running `zig build`). // running `zig build`).
b.installArtifact(lib); //b.installArtifact(lib);
const exe = b.addExecutable(.{ const exe = b.addExecutable(.{
.name = "csvu", .name = "csvu",
.root_source_file = b.path("src/main.zig"), .root_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, .imports = &.{.{ .name = "csvu", .module = lib }} }),
.target = target,
.optimize = optimize,
}); });
// This declares intent for the executable to be installed into the // This declares intent for the executable to be installed into the
@@ -67,17 +64,21 @@ pub fn build(b: *std.Build) void {
// Creates a step for unit testing. This only builds the test executable // Creates a step for unit testing. This only builds the test executable
// but does not run it. // but does not run it.
const lib_unit_tests = b.addTest(.{ const lib_unit_tests = b.addTest(.{
.root_source_file = b.path("src/root.zig"), .root_module = b.createModule(.{
.target = target, .root_source_file = b.path("src/root.zig"),
.optimize = optimize, .target = target,
.optimize = optimize,
}),
}); });
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
const exe_unit_tests = b.addTest(.{ const exe_unit_tests = b.addTest(.{
.root_source_file = b.path("src/main.zig"), .root_module = b.createModule(.{
.target = target, .root_source_file = b.path("src/main.zig"),
.optimize = optimize, .target = target,
.optimize = optimize,
}),
}); });
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);

View File

@@ -5,16 +5,6 @@ const CsvError = error{
NoDelimiterFound, NoDelimiterFound,
}; };
fn concat(one: []const u8, two: []const u8) ![]const u8 {
const allocator = std.heap.page_allocator;
var result = try allocator.alloc(u8, one.len + two.len);
std.mem.copyForwards(u8, result[0..], one);
std.mem.copyForwards(u8, result[one.len..], two);
return result;
}
fn contains(arr: []const u8, target: u8) bool { fn contains(arr: []const u8, target: u8) bool {
for (arr) |element| { for (arr) |element| {
if (element == target) { if (element == target) {
@@ -26,10 +16,13 @@ fn contains(arr: []const u8, target: u8) bool {
} }
pub fn determineDelimiter(str: []const u8) !u8 { pub fn determineDelimiter(str: []const u8) !u8 {
const alloc = std.heap.page_allocator; var allocator = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = allocator.deinit();
const alloc = allocator.allocator();
const possibleDelimiter = [_]u8{ ',', ';', '\t', '|' }; const possibleDelimiter = [_]u8{ ',', ';', '\t', '|' };
var countMap = std.AutoHashMap(u8, u32).init(alloc); var countMap = std.AutoHashMap(u8, u32).init(alloc);
defer countMap.deinit();
for (possibleDelimiter) |del| { for (possibleDelimiter) |del| {
try countMap.put(del, 0); try countMap.put(del, 0);
@@ -66,6 +59,8 @@ pub fn determineDelimiter(str: []const u8) !u8 {
const CsvFile = struct { const CsvFile = struct {
header: std.ArrayList([]const u8), header: std.ArrayList([]const u8),
entries: std.ArrayList(std.ArrayList([]const u8)), entries: std.ArrayList(std.ArrayList([]const u8)),
alloc: std.mem.Allocator,
p_lines: std.ArrayList([]const u8),
pub fn isValid(self: CsvFile) bool { pub fn isValid(self: CsvFile) bool {
const colNum = self.header.items.len; const colNum = self.header.items.len;
@@ -77,15 +72,29 @@ const CsvFile = struct {
return true; return true;
} }
pub fn deinit(self: *CsvFile) void {
for (self.p_lines.items) |line| {
self.alloc.free(line);
}
self.p_lines.deinit(self.alloc);
self.header.deinit(self.alloc);
for (self.entries.items) |entry_c| {
var entry = entry_c;
entry.deinit(self.alloc);
}
self.entries.deinit(self.alloc);
}
}; };
pub fn printTable(file: CsvFile) !void { pub fn printTable(file: CsvFile) !void {
const stdout_file = std.io.getStdOut().writer(); var stdout_buf: [1024]u8 = undefined;
var bw = std.io.bufferedWriter(stdout_file); var stdout_writer = std.fs.File.stdout().writer(&stdout_buf);
const stdout = bw.writer(); var stdout = &stdout_writer.interface;
defer { defer {
_ = bw.flush() catch null; _ = stdout.flush() catch null;
} }
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@@ -136,7 +145,6 @@ pub fn printTable(file: CsvFile) !void {
} }
_ = try stdout.writeAll("\n"); _ = try stdout.writeAll("\n");
_ = try bw.flush();
for (0..complete_length) |_| { for (0..complete_length) |_| {
try stdout.print("-", .{}); try stdout.print("-", .{});
@@ -144,22 +152,25 @@ pub fn printTable(file: CsvFile) !void {
try stdout.print("\n", .{}); try stdout.print("\n", .{});
for (file.entries.items) |entry| { for (file.entries.items) |entry| {
var out_line: []const u8 = "|"; var out_line = std.ArrayList(u8){};
defer out_line.deinit(alloc);
try out_line.appendSlice(alloc, "|");
for (0..col_nums) |i| { for (0..col_nums) |i| {
const out = entry.items[i]; const out = entry.items[i];
const missing = col_sizes[i] - out.len; const missing = col_sizes[i] - out.len;
out_line = try concat(out_line, out); try out_line.appendSlice(alloc, out);
for (0..missing) |_| { for (0..missing) |_| {
out_line = try concat(out_line, " "); try out_line.appendSlice(alloc, " ");
} }
out_line = try concat(out_line, "|"); try out_line.appendSlice(alloc, "|");
} }
out_line = try concat(out_line, "\n"); try out_line.appendSlice(alloc, "\n");
_ = try stdout.writeAll(out_line);
_ = try bw.flush(); _ = try stdout.writeAll(out_line.items);
} }
for (0..complete_length) |_| { for (0..complete_length) |_| {
try stdout.print("-", .{}); try stdout.print("-", .{});
@@ -167,35 +178,45 @@ pub fn printTable(file: CsvFile) !void {
try stdout.print("\n", .{}); try stdout.print("\n", .{});
} }
pub fn loadFile(filepath: []const u8) !CsvFile { pub fn loadFile(filepath: []const u8, alloc: std.mem.Allocator) !CsvFile {
const alloc = std.heap.page_allocator; var file_buf: [4096]u8 = undefined;
var file = try std.fs.cwd().openFile(filepath, .{});
defer file.close();
var buf_reader = std.io.bufferedReader(file.reader()); var file = try std.fs.cwd().openFile(filepath, .{ .mode = .read_write });
var in_stream = buf_reader.reader(); defer file.close();
var buffer: [4096]u8 = undefined; var file_reader = file.reader(&file_buf);
const in_stream = &file_reader.interface;
var readHeader = false; var readHeader = false;
var headerList: std.ArrayList([]const u8) = undefined; var headerList: std.ArrayList([]const u8) = undefined;
var entries = std.ArrayList(std.ArrayList([]const u8)).init(alloc); var entries = std.ArrayList(std.ArrayList([]const u8)){};
var lines = std.ArrayList([]const u8){};
var delimiter: u8 = ' '; var delimiter: u8 = ' ';
while (try in_stream.readUntilDelimiterOrEof(&buffer, '\n')) |line| { while (true) {
const line = in_stream.takeDelimiterExclusive('\n') catch |err| {
if (err == error.EndOfStream) {
break;
}
return err;
};
if (delimiter == ' ') { if (delimiter == ' ') {
delimiter = try determineDelimiter(line); delimiter = try determineDelimiter(line);
} }
const del = delimiter; const del = delimiter;
var entr = std.ArrayList([]const u8).init(alloc); var entr = std.ArrayList([]const u8){};
var splitIt = std.mem.splitSequence(u8, line, &[_]u8{del}); var splitIt = std.mem.splitSequence(u8, line, &[_]u8{del});
while (splitIt.next()) |part| { while (splitIt.next()) |part| {
const dest = try alloc.alloc(u8, part.len); const dest = try alloc.alloc(u8, part.len);
lines.append(alloc, dest) catch unreachable;
std.mem.copyForwards(u8, dest, part); std.mem.copyForwards(u8, dest, part);
const res = std.mem.trim(u8, dest, &[_]u8{ '\n', '\t', '\r', ' ' }); const res = std.mem.trim(u8, dest, &[_]u8{ '\n', '\t', '\r', ' ' });
_ = try entr.append(res);
_ = try entr.append(alloc, res);
} }
if (!readHeader) { if (!readHeader) {
@@ -203,10 +224,10 @@ pub fn loadFile(filepath: []const u8) !CsvFile {
readHeader = true; readHeader = true;
continue; continue;
} }
_ = try entries.append(entr); _ = try entries.append(alloc, entr);
} }
return CsvFile{ .entries = entries, .header = headerList }; return CsvFile{ .entries = entries, .header = headerList, .alloc = alloc, .p_lines = lines };
} }
test "Determine delimiter" { test "Determine delimiter" {

View File

@@ -2,12 +2,17 @@ const std = @import("std");
const csv = @import("csv.zig"); const csv = @import("csv.zig");
pub fn main() !void { pub fn main() !void {
const stdout_file = std.io.getStdOut().writer(); var stdout_buf: [1024]u8 = undefined;
var bw = std.io.bufferedWriter(stdout_file); var stdout_writer = std.fs.File.stdout().writer(&stdout_buf);
const stdout = bw.writer(); var stdout = &stdout_writer.interface;
const alloc = std.heap.page_allocator;
var allocator = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = allocator.deinit();
const alloc = allocator.allocator();
var args = try std.process.ArgIterator.initWithAllocator(alloc); var args = try std.process.ArgIterator.initWithAllocator(alloc);
defer args.deinit();
_ = args.next(); _ = args.next();
var filepath: [:0]const u8 = ""; var filepath: [:0]const u8 = "";
@@ -18,16 +23,18 @@ pub fn main() !void {
if (std.mem.eql(u8, filepath, "")) { if (std.mem.eql(u8, filepath, "")) {
_ = try stdout.write("No file specified"); _ = try stdout.write("No file specified");
_ = try bw.flush(); _ = try stdout.flush();
return; return;
} }
var file = try csv.loadFile(filepath); var file = try csv.loadFile(filepath, alloc);
defer file.deinit();
const valid = file.isValid(); const valid = file.isValid();
if (!valid) return; if (!valid) return;
_ = try bw.flush(); _ = try stdout.flush();
try csv.printTable(file); try csv.printTable(file);
_ = try bw.flush(); _ = try stdout.flush();
} }

View File

@@ -9,15 +9,18 @@ pub fn getTerminalDimensions() !Dimensions {
const os_tag = builtin.os.tag; const os_tag = builtin.os.tag;
switch (os_tag) { switch (os_tag) {
.linux, .macos, .freebsd, .openbsd, .netbsd => { .linux, .freebsd, .openbsd, .netbsd => {
return try getUnixTerminalDimensions(); return try getUnixTerminalDimensions();
}, },
.windows => { .windows => {
return try getWindowsTerminalDimensions(); return try getWindowsTerminalDimensions();
}, },
.macos => {
return try getMacOsTerminalDimensions();
},
else => { else => {
return Error.UnsupportedOs; return Error.UnsupportedOs;
} },
} }
} }
@@ -32,6 +35,18 @@ fn getUnixTerminalDimensions() !Dimensions {
return Dimensions{ .width = size.ws_col, .height = size.ws_row }; return Dimensions{ .width = size.ws_col, .height = size.ws_row };
} }
pub fn getMacOsTerminalDimensions() !Dimensions {
var ws: std.posix.winsize = undefined;
const fd = std.io.getStdOut().handle;
_ = std.c.ioctl(fd, std.posix.T.IOCGWINSZ, @intFromPtr(&ws));
return Dimensions{
.width = ws.col,
.height = ws.row,
};
}
fn getWindowsTerminalDimensions() !Dimensions { fn getWindowsTerminalDimensions() !Dimensions {
const win = std.os.windows; const win = std.os.windows;
const handle = win.GetStdHandle(win.STD_OUTPUT_HANDLE) catch return Error.ErrorFetchingDimensions; const handle = win.GetStdHandle(win.STD_OUTPUT_HANDLE) catch return Error.ErrorFetchingDimensions;