From c8efdbdffe176fa17cbfdceffe00f29f5e1f9736 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Sun, 1 Sep 2024 10:44:50 +0200 Subject: [PATCH] Adding Base32 decode method It can currently decode when the encoded string is a multiple of 8 bytes. --- src/base32.zig | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/base32.zig diff --git a/src/base32.zig b/src/base32.zig new file mode 100644 index 0000000..99c0cfb --- /dev/null +++ b/src/base32.zig @@ -0,0 +1,84 @@ +const std = @import("std"); +const expect = std.testing.expect; +const expectEqualStrings = std.testing.expectEqualStrings; +const Allocator = std.mem.Allocator; + +const Base32Error = error{InvalidLength}; + +const Base32 = struct { + /// + pub fn decodeU8(allocator: Allocator, data: []const u8) ![]const u8 { + const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + if (data.len % 8 != 0) { + return error.InvalidLength; + } + + const result = try allocator.alloc(u8, (data.len / 8) * 5); + + var i: u64 = 0; + var r: usize = 0; + while (i < data.len) { + var bytes: u40 = 0; + const v1: u8 = @truncate(std.mem.indexOfScalar(u8, alphabet, data[i]).?); + const v2: u8 = @truncate(std.mem.indexOfScalar(u8, alphabet, data[i + 1]).?); + const v3: u8 = @truncate(std.mem.indexOfScalar(u8, alphabet, data[i + 2]).?); + const v4: u8 = @truncate(std.mem.indexOfScalar(u8, alphabet, data[i + 3]).?); + const v5: u8 = @truncate(std.mem.indexOfScalar(u8, alphabet, data[i + 4]).?); + const v6: u8 = @truncate(std.mem.indexOfScalar(u8, alphabet, data[i + 5]).?); + const v7: u8 = @truncate(std.mem.indexOfScalar(u8, alphabet, data[i + 6]).?); + const v8: u8 = @truncate(std.mem.indexOfScalar(u8, alphabet, data[i + 7]).?); + bytes = v1; + bytes = bytes << 5 | v2; + bytes = bytes << 5 | v3; + bytes = bytes << 5 | v4; + bytes = bytes << 5 | v5; + bytes = bytes << 5 | v6; + bytes = bytes << 5 | v7; + bytes = bytes << 5 | v8; + i += 8; + + result[r] = @as(u8, @truncate((bytes >> 32) & 0b11111111)); + r += 1; + result[r] = @as(u8, @truncate((bytes >> 24) & 0b11111111)); + r += 1; + result[r] = @as(u8, @truncate((bytes >> 16) & 0b11111111)); + r += 1; + result[r] = @as(u8, @truncate((bytes >> 8) & 0b11111111)); + r += 1; + result[r] = @as(u8, @truncate((bytes) & 0b11111111)); + r += 1; + } + + return result; + } +}; + +test "base32 decode base32('abcde') " { + try testDecode("abcde", "MFRGGZDF"); +} +test "base32 decode base32('aaaaa') " { + try testDecode("aaaaa", "MFQWCYLB"); +} + +fn testDecode(expected_decoded: []const u8, encoded: []const u8) !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + + const decoded = try Base32.decodeU8(allocator, encoded); + try expectEqualStrings(expected_decoded, decoded); +} + +test "base32 decode - invalid length" { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + + try std.testing.expect(Base32.decodeU8(allocator, "1") == error.InvalidLength); + try std.testing.expect(Base32.decodeU8(allocator, "12") == error.InvalidLength); + try std.testing.expect(Base32.decodeU8(allocator, "123") == error.InvalidLength); + try std.testing.expect(Base32.decodeU8(allocator, "1234") == error.InvalidLength); + try std.testing.expect(Base32.decodeU8(allocator, "12345") == error.InvalidLength); + try std.testing.expect(Base32.decodeU8(allocator, "123456") == error.InvalidLength); + try std.testing.expect(Base32.decodeU8(allocator, "1234567") == error.InvalidLength); + try std.testing.expect(Base32.decodeU8(allocator, "123456789") == error.InvalidLength); +}