Skip to content

Commit 918f16d

Browse files
authored
Merge pull request #17 from emneo-dev/master
Finish zig 0.15 port
2 parents bc7809e + a6a5b44 commit 918f16d

File tree

9 files changed

+149
-112
lines changed

9 files changed

+149
-112
lines changed

README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,22 @@ This is a very simple ini-parser library that provides:
1212
### Zig
1313

1414
```zig
15-
const std = @import("std");
16-
const ini = @import("ini");
17-
1815
pub fn main() !void {
1916
const file = try std.fs.cwd().openFile("example.ini", .{});
2017
defer file.close();
2118
2219
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
2320
defer if (gpa.deinit() != .ok) @panic("memory leaked");
24-
var parser = ini.parse(gpa.allocator(), file.reader(), ";#");
21+
22+
var read_buffer: [1024]u8 = undefined;
23+
var file_reader = file.reader(&read_buffer);
24+
var parser = ini.parse(gpa.allocator(), &file_reader.interface, ";#");
2525
defer parser.deinit();
2626
27-
var writer = std.io.getStdOut().writer();
27+
var write_buffer: [1024]u8 = undefined;
28+
var file_writer = std.fs.File.stdout().writer(&write_buffer);
29+
var writer = &file_writer.interface;
30+
defer writer.flush() catch @panic("Could not flush to stdout");
2831
2932
while (try parser.next()) |record| {
3033
switch (record) {
@@ -45,12 +48,13 @@ pub fn main() !void {
4548
#include <stdbool.h>
4649

4750
int main() {
48-
FILE * f = fopen("example.ini", "rb");
51+
FILE * f = fopen("example.ini", "r");
4952
if(!f)
5053
return 1;
5154

5255
struct ini_Parser parser;
53-
ini_create_file(&parser, f, ";#", 2);
56+
char read_buffer[1024] = {0};
57+
ini_create_file(&parser, read_buffer, sizeof read_buffer, f, ";#", 2);
5458

5559
struct ini_Record record;
5660
while(true)

build.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub fn build(b: *std.Build) void {
66

77
_ = b.addModule("ini", .{
88
.root_source_file = b.path("src/ini.zig"),
9+
.optimize = optimize,
10+
.target = target,
911
});
1012

1113
const lib = b.addLibrary(.{

example/example.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
#include <stdbool.h>
55

66
int main() {
7-
FILE * f = fopen("example.ini", "rb");
7+
FILE * f = fopen("example.ini", "r");
88
if(!f)
99
return 1;
1010

1111
struct ini_Parser parser;
12-
ini_create_file(&parser, f, ";#", 2);
12+
char read_buffer[1024] = {0};
13+
ini_create_file(&parser, read_buffer, sizeof read_buffer, f, ";#", 2);
1314

1415
struct ini_Record record;
1516
while(true)

example/example.zig

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ pub fn main() !void {
77

88
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
99
defer if (gpa.deinit() != .ok) @panic("memory leaked");
10-
var parser = ini.parse(gpa.allocator(), file.reader(), ";#");
10+
11+
var read_buffer: [1024]u8 = undefined;
12+
var file_reader = file.reader(&read_buffer);
13+
var parser = ini.parse(gpa.allocator(), &file_reader.interface, ";#");
1114
defer parser.deinit();
1215

13-
var writer = std.io.getStdOut().writer();
16+
var write_buffer: [1024]u8 = undefined;
17+
var file_writer = std.fs.File.stdout().writer(&write_buffer);
18+
var writer = &file_writer.interface;
19+
defer writer.flush() catch @panic("Could not flush to stdout");
1420

1521
while (try parser.next()) |record| {
1622
switch (record) {

src/ini.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
/// after being initialized with `ini_create_*`!
1212
struct ini_Parser
1313
{
14-
alignas(16) char opaque[128];
14+
alignas(16) char opaque[256];
1515
};
1616

1717
enum ini_RecordType : int
@@ -56,6 +56,8 @@ extern void ini_create_buffer(
5656

5757
extern void ini_create_file(
5858
struct ini_Parser * parser,
59+
char * read_buffer,
60+
size_t read_buffer_length,
5961
FILE * file,
6062
char const * comment_characters,
6163
size_t comment_characters_length

src/ini.zig

Lines changed: 74 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -34,85 +34,96 @@ fn insertNulTerminator(slice: []const u8) [:0]const u8 {
3434
return mut_ptr[0..slice.len :0];
3535
}
3636

37-
pub fn Parser(comptime Reader: type) type {
38-
return struct {
39-
const Self = @This();
40-
41-
allocator: std.mem.Allocator,
42-
line_buffer: std.array_list.Managed(u8),
43-
reader: Reader,
44-
comment_characters: []const u8,
45-
46-
pub fn deinit(self: *Self) void {
47-
self.line_buffer.deinit();
48-
self.* = undefined;
49-
}
50-
51-
pub fn next(self: *Self) !?Record {
52-
while (true) {
53-
self.reader.readUntilDelimiterArrayList(&self.line_buffer, '\n', 4096) catch |err| switch (err) {
37+
pub const Parser = struct {
38+
const Self = @This();
39+
40+
allocator: std.mem.Allocator,
41+
line_buffer: std.array_list.Managed(u8),
42+
reader: *std.io.Reader,
43+
comment_characters: []const u8,
44+
45+
pub fn deinit(self: *Self) void {
46+
self.line_buffer.deinit();
47+
self.* = undefined;
48+
}
49+
50+
pub fn next(self: *Self) !?Record {
51+
var write_buffer: [1024]u8 = undefined;
52+
var old_writer_adapter = self.line_buffer.writer().adaptToNewApi(&write_buffer);
53+
var writer = &old_writer_adapter.new_interface;
54+
self.line_buffer.clearRetainingCapacity();
55+
while (true) {
56+
_ = try self.reader.streamDelimiterLimit(writer, '\n', .limited(4096));
57+
try writer.flush();
58+
const discarded = self.reader.discard(.limited(1)) catch |e| blk: {
59+
switch (e) {
5460
error.EndOfStream => {
5561
if (self.line_buffer.items.len == 0)
5662
return null;
63+
break :blk 0;
5764
},
58-
else => |e| return e,
59-
};
60-
try self.line_buffer.append(0); // append guaranteed space for sentinel
61-
62-
var line: []const u8 = self.line_buffer.items;
63-
var last_index: usize = 0;
64-
65-
// handle comments and escaping
66-
while (last_index < line.len) {
67-
if (std.mem.indexOfAnyPos(u8, line, last_index, self.comment_characters)) |index| {
68-
// escape character if needed, then skip it (it's not a comment)
69-
if (index > 0) {
70-
const previous_index = index - 1;
71-
const previous_char = line[previous_index];
72-
73-
if (previous_char == '\\') {
74-
_ = self.line_buffer.orderedRemove(previous_index);
75-
line = self.line_buffer.items;
76-
77-
last_index = index + 1;
78-
continue;
79-
}
65+
else => return e,
66+
}
67+
};
68+
if (self.line_buffer.items.len == 0 and discarded == 0)
69+
return null;
70+
try self.line_buffer.append(0); // append guaranteed space for sentinel
71+
72+
var line: []const u8 = self.line_buffer.items;
73+
var last_index: usize = 0;
74+
75+
// handle comments and escaping
76+
while (last_index < line.len) {
77+
if (std.mem.indexOfAnyPos(u8, line, last_index, self.comment_characters)) |index| {
78+
// escape character if needed, then skip it (it's not a comment)
79+
if (index > 0) {
80+
const previous_index = index - 1;
81+
const previous_char = line[previous_index];
82+
83+
if (previous_char == '\\') {
84+
_ = self.line_buffer.orderedRemove(previous_index);
85+
line = self.line_buffer.items;
86+
87+
last_index = index + 1;
88+
continue;
8089
}
81-
82-
line = std.mem.trim(u8, line[0..index], whitespace);
83-
} else {
84-
line = std.mem.trim(u8, line, whitespace);
8590
}
8691

87-
break;
92+
line = std.mem.trim(u8, line[0..index], whitespace);
93+
} else {
94+
line = std.mem.trim(u8, line, whitespace);
8895
}
8996

90-
if (line.len == 0)
91-
continue;
97+
break;
98+
}
9299

93-
if (std.mem.startsWith(u8, line, "[") and std.mem.endsWith(u8, line, "]")) {
94-
return Record{ .section = insertNulTerminator(line[1 .. line.len - 1]) };
95-
}
100+
if (line.len == 0) {
101+
self.line_buffer.clearRetainingCapacity();
102+
continue;
103+
}
96104

97-
if (std.mem.indexOfScalar(u8, line, '=')) |index| {
98-
return Record{
99-
.property = KeyValue{
100-
// note: the key *might* replace the '=' in the slice with 0!
101-
.key = insertNulTerminator(std.mem.trim(u8, line[0..index], whitespace)),
102-
.value = insertNulTerminator(std.mem.trim(u8, line[index + 1 ..], whitespace)),
103-
},
104-
};
105-
}
105+
if (std.mem.startsWith(u8, line, "[") and std.mem.endsWith(u8, line, "]")) {
106+
return Record{ .section = insertNulTerminator(line[1 .. line.len - 1]) };
107+
}
106108

107-
return Record{ .enumeration = insertNulTerminator(line) };
109+
if (std.mem.indexOfScalar(u8, line, '=')) |index| {
110+
return Record{
111+
.property = KeyValue{
112+
// note: the key *might* replace the '=' in the slice with 0!
113+
.key = insertNulTerminator(std.mem.trim(u8, line[0..index], whitespace)),
114+
.value = insertNulTerminator(std.mem.trim(u8, line[index + 1 ..], whitespace)),
115+
},
116+
};
108117
}
118+
119+
return Record{ .enumeration = insertNulTerminator(line) };
109120
}
110-
};
111-
}
121+
}
122+
};
112123

113124
/// Returns a new parser that can read the ini structure
114-
pub fn parse(allocator: std.mem.Allocator, reader: anytype, comment_characters: []const u8) Parser(@TypeOf(reader)) {
115-
return Parser(@TypeOf(reader)){
125+
pub fn parse(allocator: std.mem.Allocator, reader: *std.io.Reader, comment_characters: []const u8) Parser {
126+
return Parser{
116127
.allocator = allocator,
117128
.line_buffer = std.array_list.Managed(u8).init(allocator),
118129
.reader = reader,

src/lib-test.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ test "file parser" {
8282
defer _ = c.fclose(file);
8383

8484
var parser: c.ini_Parser = undefined;
85-
c.ini_create_file(&parser, file, ";#", 2);
85+
var read_buffer: [1024]u8 = undefined;
86+
c.ini_create_file(&parser, &read_buffer, read_buffer.len, file, ";#", 2);
8687
defer c.ini_destroy(&parser);
8788

8889
try commonTest(&parser);

src/lib.zig

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ const Record = extern struct {
2929
};
3030

3131
const BufferParser = struct {
32-
stream: std.io.FixedBufferStream([]const u8),
33-
parser: ini.Parser(std.io.FixedBufferStream([]const u8).Reader),
32+
stream: std.io.Reader,
33+
parser: ini.Parser,
34+
};
35+
36+
const FileParser = struct {
37+
old_reader_adapter: CReader.Adapter,
38+
parser: ini.Parser,
3439
};
3540

3641
const IniParser = union(enum) {
3742
buffer: BufferParser,
38-
file: ini.Parser(CReader),
43+
file: FileParser,
3944
};
4045

4146
const IniError = enum(c.ini_Error) {
@@ -65,29 +70,34 @@ comptime {
6570
export fn ini_create_buffer(parser: *IniParser, data: [*]const u8, data_length: usize, comment_characters: [*]const u8, comment_characters_length: usize) void {
6671
parser.* = IniParser{
6772
.buffer = .{
68-
.stream = std.io.fixedBufferStream(data[0..data_length]),
73+
.stream = std.io.Reader.fixed(data[0..data_length]),
6974
.parser = undefined,
7075
},
7176
};
7277
// this is required to have the parser store a pointer to the stream.
73-
parser.buffer.parser = ini.parse(std.heap.c_allocator, parser.buffer.stream.reader(), comment_characters[0..comment_characters_length]);
78+
parser.buffer.parser = ini.parse(std.heap.c_allocator, &parser.buffer.stream, comment_characters[0..comment_characters_length]);
7479
}
7580

76-
export fn ini_create_file(parser: *IniParser, file: *std.c.FILE, comment_characters: [*]const u8, comment_characters_length: usize) void {
81+
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 {
7782
parser.* = IniParser{
78-
.file = ini.parse(std.heap.c_allocator, cReader(file), comment_characters[0..comment_characters_length]),
83+
.file = .{
84+
.old_reader_adapter = cReader(file).adaptToNewApi(read_buffer[0..read_buffer_length]),
85+
.parser = undefined,
86+
},
7987
};
88+
89+
parser.file.parser = ini.parse(std.heap.c_allocator, &parser.file.old_reader_adapter.new_interface, comment_characters[0..comment_characters_length]);
8090
}
8191

8292
export fn ini_destroy(parser: *IniParser) void {
8393
switch (parser.*) {
8494
.buffer => |*p| p.parser.deinit(),
85-
.file => |*p| p.deinit(),
95+
.file => |*p| p.parser.deinit(),
8696
}
8797
parser.* = undefined;
8898
}
8999

90-
const ParseError = error{ OutOfMemory, StreamTooLong } || CReader.Error;
100+
const ParseError = error{ OutOfMemory, StreamTooLong } || std.io.Reader.Error || std.io.Writer.Error;
91101

92102
fn mapError(err: ParseError) IniError {
93103
return switch (err) {
@@ -100,7 +110,7 @@ fn mapError(err: ParseError) IniError {
100110
export fn ini_next(parser: *IniParser, record: *Record) IniError {
101111
const src_record_or_null: ?ini.Record = switch (parser.*) {
102112
.buffer => |*p| p.parser.next() catch |e| return mapError(e),
103-
.file => |*p| p.next() catch |e| return mapError(e),
113+
.file => |*p| p.parser.next() catch |e| return mapError(e),
104114
};
105115

106116
if (src_record_or_null) |src_record| {

0 commit comments

Comments
 (0)