aboutsummaryrefslogtreecommitdiff
path: root/src/bencode.zig
diff options
context:
space:
mode:
authorMartin Ashby <martin@ashbysoft.com>2023-11-09 23:58:32 +0000
committerMartin Ashby <martin@ashbysoft.com>2023-11-09 23:58:32 +0000
commit2d65d9d3515a523d9cb8d242c3fc89671ae97d63 (patch)
treea568a510a72f4aaa484344e2e7b6a9b76a348727 /src/bencode.zig
parent68c104c8b0580c51c9f16ec33d6a957fd4c08c0c (diff)
downloadzbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.tar.gz
zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.tar.bz2
zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.tar.xz
zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.zip
Add bencoding as well as bdecoding
Diffstat (limited to 'src/bencode.zig')
-rw-r--r--src/bencode.zig50
1 files changed, 50 insertions, 0 deletions
diff --git a/src/bencode.zig b/src/bencode.zig
index b78ee9f..44dec0d 100644
--- a/src/bencode.zig
+++ b/src/bencode.zig
@@ -2,6 +2,7 @@
//! See specification here https://wiki.theory.org/BitTorrentSpecification#Bencoding
const std = @import("std");
+const AnyWriter = @import("anywriter.zig");
pub const Error = error.Malformatted || std.io.AnyReader.Error;
@@ -12,6 +13,41 @@ pub const BValue = union(enum) {
list: std.ArrayList(BValue),
dict: std.StringArrayHashMap(BValue),
+ pub fn bencode(self: *const BValue, base_writer: anytype) !void {
+ var wrap = AnyWriter.wrapper(base_writer);
+ var writer = wrap.any();
+ try self.bencodeInner(writer);
+ }
+
+ // Note: uses defined types only to avoid trying to recursively evaulate this function
+ // at compile time, otherwise we run into https://github.com/ziglang/zig/issues/13724
+ fn bencodeInner(self: *const BValue, writer: AnyWriter) !void {
+ switch (self.*) {
+ .string => |s| {
+ try std.fmt.format(writer, "{}:{s}", .{ s.len, s });
+ },
+ .list => |l| {
+ try writer.writeByte('l');
+ for (l.items) |i| {
+ try i.bencodeInner(writer);
+ }
+ try writer.writeByte('e');
+ },
+ .dict => |d| {
+ try writer.writeByte('d');
+ var it = d.iterator();
+ while (it.next()) |entry| {
+ try std.fmt.format(writer, "{}:{s}", .{ entry.key_ptr.*.len, entry.key_ptr.* });
+ try entry.value_ptr.*.bencodeInner(writer);
+ }
+ try writer.writeByte('e');
+ },
+ .int => |i| {
+ try std.fmt.format(writer, "i{}e", .{i});
+ },
+ }
+ }
+
pub fn deinit(self: *BValue, a: std.mem.Allocator) void {
switch (self.*) {
.string => |s| {
@@ -237,3 +273,17 @@ test "nested structure" {
try std.testing.expectEqualDeep(v2.*.list.items[1], BValue{ .int = 456 });
try std.testing.expectEqualStrings("nest", v2.*.list.items[2].list.items[0].string);
}
+
+test "round trip" {
+ var a = std.testing.allocator;
+ const in = "d5:hello5:world2:hili123ei456el4:nesteee";
+ var bval = try bdecodeBuf(a, in);
+ defer bval.deinit(a);
+ var bw = std.ArrayList(u8).init(a);
+ defer bw.deinit();
+ var writer = bw.writer();
+ try bval.bencode(writer);
+ var out = try bw.toOwnedSlice();
+ defer a.free(out);
+ try std.testing.expectEqualStrings(in, out);
+}