aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Ashby <martin@ashbysoft.com>2023-11-11 11:36:26 +0000
committerMartin Ashby <martin@ashbysoft.com>2023-11-11 11:36:26 +0000
commitdff4234bf2d957a0328ff4f3dc4f9bba1fbeffd4 (patch)
treeaee908b8552c203b754ba9c62c40262dc0cdef09
parent7eff166e1f7b440392be1082e3edd0c38b92d77c (diff)
downloadzbt-dff4234bf2d957a0328ff4f3dc4f9bba1fbeffd4.tar.gz
zbt-dff4234bf2d957a0328ff4f3dc4f9bba1fbeffd4.tar.bz2
zbt-dff4234bf2d957a0328ff4f3dc4f9bba1fbeffd4.tar.xz
zbt-dff4234bf2d957a0328ff4f3dc4f9bba1fbeffd4.zip
Extend meta info file parsing
-rw-r--r--src/bencode.zig7
-rw-r--r--src/metainfo.zig54
2 files changed, 54 insertions, 7 deletions
diff --git a/src/bencode.zig b/src/bencode.zig
index d09f394..60a3746 100644
--- a/src/bencode.zig
+++ b/src/bencode.zig
@@ -93,6 +93,13 @@ pub const BValue = union(enum) {
else => return error.WrongType,
}
}
+
+ pub fn asList(self: BValue) !std.ArrayList(BValue) {
+ switch (self) {
+ .list => |l| return l,
+ else => return error.WrongType,
+ }
+ }
};
pub fn bdecodeBuf(a: std.mem.Allocator, buf: []const u8) !BValue {
diff --git a/src/metainfo.zig b/src/metainfo.zig
index 9fa4788..a202b0e 100644
--- a/src/metainfo.zig
+++ b/src/metainfo.zig
@@ -2,6 +2,7 @@
const std = @import("std");
const bencode = @import("bencode.zig");
const MetaInfo = @This();
+pub const Error = (error{Malformatted} || std.mem.Allocator.Error);
pub const Info = struct {
pub const File = struct {
@@ -12,36 +13,75 @@ pub const Info = struct {
piece_length: u64,
pieces: []const u8,
files: []File,
- pub fn parse(b: bencode.BValue) !Info {
+ pub fn parse(a: std.mem.Allocator, b: bencode.BValue) Error!Info {
var d = b.asDict() catch return error.Malformatted;
const pl = d.get("piece length") orelse return error.Malformatted;
const pp = d.get("pieces") orelse return error.Malformatted;
+ var files = std.ArrayList(File).init(a);
+ defer files.deinit();
+ if (d.get("files")) |f| {
+ // multi-file mode
+ const l = f.asList() catch return error.Malformatted;
+ for (l.items) |fi| {
+ const fd = fi.asDict() catch return error.Malformatted;
+ const fin = fd.get("name") orelse return error.Malformatted;
+ const fl = fd.get("length") orelse return error.Malformatted;
+ const fp = fd.get("path") orelse return error.Malformatted;
+ try files.append(.{
+ .name = fin.asString() catch return error.Malformatted,
+ .length = fl.asInt(u64) catch return error.Malformatted,
+ .path = fp.asString() catch return error.Malformatted,
+ });
+ }
+ } else {
+ // single-file mode
+ const fin = d.get("name") orelse return error.Malformatted;
+ const fl = d.get("length") orelse return error.Malformatted;
+ try files.append(.{
+ .name = fin.asString() catch return error.Malformatted,
+ .length = fl.asInt(u64) catch return error.Malformatted,
+ .path = fin.asString() catch return error.Malformatted, // just use the file name as path
+ });
+ }
+
return .{
.piece_length = pl.asInt(u64) catch return error.Malformatted,
.pieces = pp.asString() catch return error.Malformatted,
- .files = &[_]File{},
+ .files = try files.toOwnedSlice(),
};
}
+
+ pub fn deinit(self: *Info, a: std.mem.Allocator) void {
+ a.free(self.files);
+ }
};
info: Info,
announce: []const u8,
-pub fn parse(b: bencode.BValue) !MetaInfo {
+pub fn parse(a: std.mem.Allocator, b: bencode.BValue) Error!MetaInfo {
+ // TODO diagnostics
var d = b.asDict() catch return error.Malformatted;
const i = d.get("info") orelse return error.Malformatted;
- const a = d.get("announce") orelse return error.Malformatted;
+ const an = d.get("announce") orelse return error.Malformatted;
return .{
- .info = try Info.parse(i),
- .announce = a.asString() catch return error.Malformatted,
+ .info = try Info.parse(a, i),
+ .announce = an.asString() catch return error.Malformatted,
};
}
+pub fn deinit(self: *MetaInfo, a: std.mem.Allocator) void {
+ self.info.deinit(a);
+}
+
test "sample" {
const a = std.testing.allocator;
const sample_str = @embedFile("sample.torrent");
var b = try bencode.bdecodeBuf(a, sample_str);
defer b.deinit(a);
- const mi = try MetaInfo.parse(b);
+ var mi = try MetaInfo.parse(a, b);
+ defer mi.deinit(a);
try std.testing.expectEqualStrings("http://bittorrent-test-tracker.codecrafters.io/announce", mi.announce);
+ try std.testing.expectEqual(@as(usize, 1), mi.info.files.len);
+ try std.testing.expectEqualStrings("sample.txt", mi.info.files[0].name);
}