Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ jobs:
- uses: actions/checkout@v3
- uses: mlugg/setup-zig@v2
with:
version: 0.15.0
version: 0.16.0
- run: zig build test
21 changes: 9 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,19 @@ This is a very simple ini-parser library that provides:
### Zig

```zig
pub fn main() !void {
const file = try std.fs.cwd().openFile("example.ini", .{});
defer file.close();

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer if (gpa.deinit() != .ok) @panic("memory leaked");
pub fn main(init: std.process.Init) !void {
const io = init.io;
const file = try std.Io.Dir.cwd().openFile(io, "example.ini", .{});
defer file.close(io);

var read_buffer: [1024]u8 = undefined;
var file_reader = file.reader(&read_buffer);
var parser = ini.parse(gpa.allocator(), &file_reader.interface, ";#");
var file_reader = file.reader(io, &read_buffer);
var parser = ini.parse(init.gpa, &file_reader.interface, ";#");
defer parser.deinit();

var write_buffer: [1024]u8 = undefined;
var file_writer = std.fs.File.stdout().writer(&write_buffer);
var writer = &file_writer.interface;
var file_writer = std.Io.File.stdout().writer(io, &write_buffer);
const writer = &file_writer.interface;
defer writer.flush() catch @panic("Could not flush to stdout");

while (try parser.next()) |record| {
Expand All @@ -36,8 +34,7 @@ pub fn main() !void {
.enumeration => |value| try writer.print("{s}\n", .{value}),
}
}
}
```
}```

### C

Expand Down
40 changes: 30 additions & 10 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,29 @@ pub fn build(b: *std.Build) void {
.target = target,
});

const ini_c_header = b.addTranslateC(.{
.root_source_file = b.path("src/ini.h"),
.target = target,
.optimize = optimize,
});

const lib = b.addLibrary(.{
.name = "ini",
.root_module = b.createModule(.{
.root_source_file = b.path("src/lib.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
.imports = &.{
.{
.name = "c",
.module = ini_c_header.createModule(),
},
},
}),
});
lib.bundle_compiler_rt = true;
lib.addIncludePath(b.path("src"));
lib.linkLibC();
lib.root_module.addIncludePath(b.path("src"));
lib.installHeader(b.path("src/ini.h"), "ini.h");
b.installArtifact(lib);

Expand All @@ -30,19 +42,19 @@ pub fn build(b: *std.Build) void {
.root_module = b.createModule(.{
.optimize = optimize,
.target = target,
.link_libc = true,
}),
});
example_c.addCSourceFile(.{
example_c.root_module.addCSourceFile(.{
.file = b.path("example/example.c"),
.flags = &.{
"-Wall",
"-Wextra",
"-pedantic",
},
});
example_c.addIncludePath(b.path("src"));
example_c.linkLibrary(lib);
example_c.linkLibC();
example_c.root_module.addIncludePath(b.path("src"));
example_c.root_module.linkLibrary(lib);
example_step.dependOn(&b.addInstallArtifact(example_c, .{}).step);

const example_zig = b.addExecutable(.{
Expand All @@ -51,9 +63,11 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("example/example.zig"),
.optimize = optimize,
.target = target,
.imports = &.{
.{ .name = "ini", .module = b.modules.get("ini").? },
},
}),
});
example_zig.root_module.addImport("ini", b.modules.get("ini").?);
example_step.dependOn(&b.addInstallArtifact(example_zig, .{}).step);

const test_step = b.step("test", "Run library tests");
Expand All @@ -71,10 +85,16 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("src/lib-test.zig"),
.optimize = optimize,
.target = target,
.link_libc = true,
.imports = &.{
.{
.name = "c",
.module = ini_c_header.createModule(),
},
},
}),
});
binding_tests.addIncludePath(b.path("src"));
binding_tests.linkLibrary(lib);
binding_tests.linkLibC();
binding_tests.root_module.addIncludePath(b.path("src"));
binding_tests.root_module.linkLibrary(lib);
test_step.dependOn(&b.addRunArtifact(binding_tests).step);
}
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.name = .ini,
.fingerprint = 0x7757b668623d2460,
.version = "0.1.0",
.minimum_zig_version = "0.15.0",
.minimum_zig_version = "0.16.0",
.paths = .{
"LICENCE",
"README.md",
Expand Down
18 changes: 8 additions & 10 deletions example/example.zig
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
const std = @import("std");
const ini = @import("ini");

pub fn main() !void {
const file = try std.fs.cwd().openFile("example.ini", .{});
defer file.close();

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer if (gpa.deinit() != .ok) @panic("memory leaked");
pub fn main(init: std.process.Init) !void {
const io = init.io;
const file = try std.Io.Dir.cwd().openFile(io, "example.ini", .{});
defer file.close(io);

var read_buffer: [1024]u8 = undefined;
var file_reader = file.reader(&read_buffer);
var parser = ini.parse(gpa.allocator(), &file_reader.interface, ";#");
var file_reader = file.reader(io, &read_buffer);
var parser = ini.parse(init.gpa, &file_reader.interface, ";#");
defer parser.deinit();

var write_buffer: [1024]u8 = undefined;
var file_writer = std.fs.File.stdout().writer(&write_buffer);
var writer = &file_writer.interface;
var file_writer = std.Io.File.stdout().writer(io, &write_buffer);
const writer = &file_writer.interface;
defer writer.flush() catch @panic("Could not flush to stdout");

while (try parser.next()) |record| {
Expand Down
25 changes: 14 additions & 11 deletions src/ini.zig
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,26 @@ pub const Parser = struct {
const Self = @This();

allocator: std.mem.Allocator,
line_buffer: std.array_list.Managed(u8),
reader: *std.io.Reader,
line_buffer: std.ArrayList(u8),
reader: *std.Io.Reader,
comment_characters: []const u8,

pub fn deinit(self: *Self) void {
self.line_buffer.deinit();
self.line_buffer.deinit(self.allocator);
self.* = undefined;
}

pub fn next(self: *Self) !?Record {
var write_buffer: [1024]u8 = undefined;
var old_writer_adapter = self.line_buffer.writer().adaptToNewApi(&write_buffer);
var writer = &old_writer_adapter.new_interface;
self.line_buffer.clearRetainingCapacity();
while (true) {
_ = try self.reader.streamDelimiterLimit(writer, '\n', .limited(4096));
try writer.flush();
{
Comment thread
ikskuh marked this conversation as resolved.
Outdated
var list_writer = std.Io.Writer.Allocating.fromArrayList(self.allocator, &self.line_buffer);
defer self.line_buffer = list_writer.toArrayList();

_ = try self.reader.streamDelimiterLimit(&list_writer.writer, '\n', .limited(4096));
try list_writer.writer.flush();
}

const discarded = self.reader.discard(.limited(1)) catch |e| blk: {
switch (e) {
error.EndOfStream => {
Expand All @@ -67,7 +70,7 @@ pub const Parser = struct {
};
if (self.line_buffer.items.len == 0 and discarded == 0)
return null;
try self.line_buffer.append(0); // append guaranteed space for sentinel
try self.line_buffer.append(self.allocator, 0); // append guaranteed space for sentinel

var line: []const u8 = self.line_buffer.items;
var last_index: usize = 0;
Expand Down Expand Up @@ -122,10 +125,10 @@ pub const Parser = struct {
};

/// Returns a new parser that can read the ini structure
pub fn parse(allocator: std.mem.Allocator, reader: *std.io.Reader, comment_characters: []const u8) Parser {
pub fn parse(allocator: std.mem.Allocator, reader: *std.Io.Reader, comment_characters: []const u8) Parser {
return Parser{
.allocator = allocator,
.line_buffer = std.array_list.Managed(u8).init(allocator),
.line_buffer = .empty,
.reader = reader,
.comment_characters = comment_characters,
};
Expand Down
5 changes: 1 addition & 4 deletions src/lib-test.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
const std = @import("std");

const c = @cImport({
@cInclude("ini.h");
});
const c = @import("c");

test "parser create/destroy" {
var buffer: c.ini_Parser = undefined;
Expand Down
70 changes: 38 additions & 32 deletions src/lib.zig
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
const std = @import("std");
const ini = @import("ini.zig");

const c = @cImport({
@cInclude("ini.h");
});
const c = @import("c");

const Record = extern struct {
type: Type,
Expand All @@ -29,12 +26,12 @@ const Record = extern struct {
};

const BufferParser = struct {
stream: std.io.Reader,
stream: std.Io.Reader,
parser: ini.Parser,
};

const FileParser = struct {
old_reader_adapter: CReader.Adapter,
reader: CReader,
parser: ini.Parser,
};

Expand Down Expand Up @@ -70,7 +67,7 @@ comptime {
export fn ini_create_buffer(parser: *IniParser, data: [*]const u8, data_length: usize, comment_characters: [*]const u8, comment_characters_length: usize) void {
parser.* = IniParser{
.buffer = .{
.stream = std.io.Reader.fixed(data[0..data_length]),
.stream = std.Io.Reader.fixed(data[0..data_length]),
.parser = undefined,
},
};
Expand All @@ -81,12 +78,12 @@ export fn ini_create_buffer(parser: *IniParser, data: [*]const u8, data_length:
export fn ini_create_file(parser: *IniParser, read_buffer: [*]u8, read_buffer_length: usize, file: *std.c.FILE, comment_characters: [*]const u8, comment_characters_length: usize) void {
parser.* = IniParser{
.file = .{
.old_reader_adapter = cReader(file).adaptToNewApi(read_buffer[0..read_buffer_length]),
.reader = CReader.init(file, read_buffer[0..read_buffer_length]),
.parser = undefined,
},
};

parser.file.parser = ini.parse(std.heap.c_allocator, &parser.file.old_reader_adapter.new_interface, comment_characters[0..comment_characters_length]);
parser.file.parser = ini.parse(std.heap.c_allocator, &parser.file.reader.interface, comment_characters[0..comment_characters_length]);
}

export fn ini_destroy(parser: *IniParser) void {
Expand All @@ -97,7 +94,7 @@ export fn ini_destroy(parser: *IniParser) void {
parser.* = undefined;
}

const ParseError = error{ OutOfMemory, StreamTooLong } || std.io.Reader.Error || std.io.Writer.Error;
const ParseError = error{ OutOfMemory, StreamTooLong } || std.Io.Reader.Error || std.Io.Writer.Error;

fn mapError(err: ParseError) IniError {
return switch (err) {
Expand Down Expand Up @@ -141,28 +138,37 @@ export fn ini_next(parser: *IniParser, record: *Record) IniError {
return .success;
}

const CReader = std.Io.GenericReader(*std.c.FILE, std.fs.File.ReadError, cReaderRead);
extern "c" fn feof(stream: *std.c.FILE) c_int;

fn cReader(c_file: *std.c.FILE) CReader {
return .{ .context = c_file };
}
const CReader = struct {
file: *std.c.FILE,
interface: std.Io.Reader,

fn cReaderRead(c_file: *std.c.FILE, bytes: []u8) std.fs.File.ReadError!usize {
const amt_read = std.c.fread(bytes.ptr, 1, bytes.len, c_file);
if (amt_read >= 0) return amt_read;
switch (@as(std.os.E, @enumFromInt(std.c._errno().*))) {
.SUCCESS => unreachable,
.INVAL => unreachable,
.FAULT => unreachable,
.AGAIN => unreachable, // this is a blocking API
.BADF => unreachable, // always a race condition
.DESTADDRREQ => unreachable, // connect was never called
.DQUOT => return error.DiskQuota,
.FBIG => return error.FileTooBig,
.IO => return error.InputOutput,
.NOSPC => return error.NoSpaceLeft,
.PERM => return error.AccessDenied,
.PIPE => return error.BrokenPipe,
else => |err| return std.os.unexpectedErrno(err),
fn init(file: *std.c.FILE, buffer: []u8) CReader {
return .{
.file = file,
.interface = .{
.vtable = &.{ .stream = stream },
.buffer = buffer,
.seek = 0,
.end = 0,
},
};
}
}

fn stream(r: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize {
const creader: *CReader = @alignCast(@fieldParentPtr("interface", r));

if (limit == .nothing) return 0;
const dest = limit.slice(try w.writableSliceGreedy(1));

const n = std.c.fread(dest.ptr, 1, dest.len, creader.file);
if (n > 0) {
w.advance(n);
return n;
}

if (feof(creader.file) != 0) return error.EndOfStream;
return error.ReadFailed;
}
};
Loading