This commit is contained in:
Evan Burkey 2024-11-07 09:37:20 -08:00
parent 607bf5021e
commit 8fb7891fe1
2 changed files with 119 additions and 115 deletions

View File

@ -5,24 +5,18 @@ pub const err = error{
IndexNotFound, IndexNotFound,
}; };
pub const Base64 = struct { fn char_at(index: u8) u8 {
table: *const [64]u8, const table: *const [64]u8 = comptime "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
return table[index];
pub const init: Base64 = .{
.table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
};
fn char_at(self: Base64, index: u8) u8 {
return self.table[index];
} }
pub fn encode(self: Base64, allocator: std.mem.Allocator, input: []const u8) ![]u8 { pub fn encode(allocator: std.mem.Allocator, input: []const u8) ![]u8 {
if (input.len == 0) { if (input.len == 0) {
return ""; return "";
} }
const encode_length = try calc_encode_length(input); const out_sz = try calc_encode_length(input);
var out = try allocator.alloc(u8, encode_length); var out = try allocator.alloc(u8, out_sz);
var buf = [3]u8{0, 0, 0}; var buf = [3]u8{0, 0, 0};
var count: u16 = 0; var count: u16 = 0;
var outc: u16 = 0; var outc: u16 = 0;
@ -31,23 +25,23 @@ pub const Base64 = struct {
buf[count] = b; buf[count] = b;
count += 1; count += 1;
if (count == 3) { if (count == 3) {
out[outc] = self.char_at(buf[0] >> 2); out[outc] = char_at(buf[0] >> 2);
out[outc + 1] = self.char_at(((buf[0] & 0x03) << 4) + (buf[1] >> 4)); out[outc + 1] = char_at(((buf[0] & 0x03) << 4) + (buf[1] >> 4));
out[outc + 2] = self.char_at(((buf[1] & 0x0F) << 2) + (buf[2] >> 6)); out[outc + 2] = char_at(((buf[1] & 0x0F) << 2) + (buf[2] >> 6));
out[outc + 3] = self.char_at(buf[2] & 0x3F); out[outc + 3] = char_at(buf[2] & 0x3F);
count = 0; count = 0;
outc += 4; outc += 4;
} }
} }
if (count == 2) { if (count == 2) {
out[outc] = self.char_at(buf[0] >> 2); out[outc] = char_at(buf[0] >> 2);
out[outc + 1] = self.char_at((buf[0] & 0x03) << 4) + (buf[1] >> 4); out[outc + 1] = char_at((buf[0] & 0x03) << 4) + (buf[1] >> 4);
out[outc + 2] = self.char_at((buf[1] & 0x0F) << 2); out[outc + 2] = char_at((buf[1] & 0x0F) << 2);
out[outc + 3] = '='; out[outc + 3] = '=';
} else if (count == 1) { } else if (count == 1) {
out[outc] = self.char_at(buf[0] >> 2); out[outc] = char_at(buf[0] >> 2);
out[outc + 1] = self.char_at((buf[0] & 0x03) << 4); out[outc + 1] = char_at((buf[0] & 0x03) << 4);
out[outc + 2] = '='; out[outc + 2] = '=';
out[outc + 3] = '='; out[outc + 3] = '=';
} }
@ -55,55 +49,59 @@ pub const Base64 = struct {
return out; return out;
} }
fn decode_index(self: Base64, char: u8) err!u8 { fn decode_index(char: u8) err!u8 {
if (char == '=') { if (char == '=') {
return 64; return 64;
} }
if (char >= 'A' and char <= 'Z') { if (char >= 'A' and char <= 'Z') {
return char - 'A'; return char - 'A';
} else if (char >= 'a' and char <= 'z') { } else if (char >= 'a' and char <= 'z') {
return char - 'a' + 26; return char - 'a' + 26;
} else { } else if (char >= '0' and char <= '9') {
for (52..64) |i| { return char - '0' + 52;
const idx: u8 = @intCast(i);
if (self.char_at(idx) == char) {
return idx;
}
}
}
return err.IndexNotFound;
} }
pub fn decode(self: Base64, allocator: std.mem.Allocator, input: []const u8) ![]u8 { switch (char) {
'+' => { return 62; },
'/' => { return 63; },
'=' => { return 64; },
else => { return err.IndexNotFound; }
}
}
pub fn decode(allocator: std.mem.Allocator, input: []const u8) ![]u8 {
if (input.len == 0) { if (input.len == 0) {
return ""; return "";
} }
const output_sz = try calc_decode_length(input); const out_sz = try calc_decode_length(input);
var output = try allocator.alloc(u8, output_sz); var out = try allocator.alloc(u8, out_sz);
var count: u8 = 0; var count: u8 = 0;
var iout: u64 = 0; var iout: u64 = 0;
var buf = [4]u8{ 0, 0, 0, 0 }; var buf = [4]u8{ 0, 0, 0, 0 };
var cutoff :u8 = 0;
for (0..input.len) |i| { for (0..input.len) |i| {
buf[count] = try self.decode_index(input[i]); buf[count] = try decode_index(input[i]);
count += 1; count += 1;
if (count == 4) { if (count == 4) {
output[iout] = (buf[0] << 2) + (buf[1] >> 4); out[iout] = (buf[0] << 2) + (buf[1] >> 4);
if (buf[2] != 64) { if (buf[2] != 64) {
output[iout + 1] = (buf[1] << 4) + (buf[2] >> 2); out[iout + 1] = (buf[1] << 4) + (buf[2] >> 2);
cutoff = 2;
} }
if (buf[3] != 64) { if (buf[3] != 64) {
output[iout + 2] = (buf[2] << 6) + buf[3]; out[iout + 2] = (buf[2] << 6) + buf[3];
cutoff = 1;
} }
iout += 3; iout += 3;
count = 0; count = 0;
} }
} }
return output; return out[0..out.len - cutoff + 1];
} }
};
fn calc_encode_length(input: []const u8) !usize { fn calc_encode_length(input: []const u8) !usize {
if (input.len < 3) { if (input.len < 3) {
@ -122,57 +120,51 @@ fn calc_decode_length(input: []const u8) !usize {
} }
test "encode hello" { test "encode hello" {
var b = Base64.init;
var gpa = std.heap.GeneralPurposeAllocator(.{}).init; var gpa = std.heap.GeneralPurposeAllocator(.{}).init;
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const encoded = try b.encode(allocator, "hello"); const encoded = try encode(allocator, "hello");
try std.testing.expect(std.mem.eql(u8, encoded, "aGVsbG8=")); try std.testing.expect(std.mem.eql(u8, encoded, "aGVsbG8="));
} }
test "encode long" { test "encode long" {
var b = Base64.init;
var gpa = std.heap.GeneralPurposeAllocator(.{}).init; var gpa = std.heap.GeneralPurposeAllocator(.{}).init;
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const encoded = try b.encode(allocator, "Hey, it's me. I'm the problem. It's me"); const encoded = try encode(allocator, "Hey, it's me. I'm the problem. It's me");
try std.testing.expect(std.mem.eql(u8, encoded, "SGV5LCBpdCdzIG1lLiBJJ20gdGhlIHByb2JsZW0uIEl0J3MgbWU=")); try std.testing.expect(std.mem.eql(u8, encoded, "SGV5LCBpdCdzIG1lLiBJJ20gdGhlIHByb2JsZW0uIEl0J3MgbWU="));
} }
test "decode hello" { test "decode hello" {
var b = Base64.init;
var gpa = std.heap.GeneralPurposeAllocator(.{}).init; var gpa = std.heap.GeneralPurposeAllocator(.{}).init;
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const encoded = try b.decode(allocator, "aGVsbG8="); const encoded = try decode(allocator, "aGVsbG8=");
try stdout.print("{s}\n", .{encoded}); try stdout.print("{s}\n", .{encoded});
try std.testing.expect(std.mem.eql(u8, encoded, "hello")); try std.testing.expect(std.mem.eql(u8, encoded, "hello"));
} }
test "decode long" { test "decode long" {
var b = Base64.init;
var gpa = std.heap.GeneralPurposeAllocator(.{}).init; var gpa = std.heap.GeneralPurposeAllocator(.{}).init;
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const encoded = try b.decode(allocator, "SGV5LCBpdCdzIG1lLiBJJ20gdGhlIHByb2JsZW0uIEl0J3MgbWU="); const encoded = try decode(allocator, "SGV5LCBpdCdzIG1lLiBJJ20gdGhlIHByb2JsZW0uIEl0J3MgbWU=");
try stdout.print("{s}\n", .{encoded}); try stdout.print("{s}\n", .{encoded});
try std.testing.expect(std.mem.eql(u8, encoded, "Hey, it's me. I'm the problem. It's me")); try std.testing.expect(std.mem.eql(u8, encoded, "Hey, it's me. I'm the problem. It's me"));
} }
test "decode_index" { test "decode_index" {
var b = Base64.init; try std.testing.expectError(err.IndexNotFound, decode_index('{'));
try std.testing.expectError(err.IndexNotFound, b.decode_index('{'));
var r :u8 = 0; var r :u8 = 0;
r = try b.decode_index('A'); r = try decode_index('A');
try std.testing.expect(r == 0); try std.testing.expect(r == 0);
r = try b.decode_index('a'); r = try decode_index('a');
try std.testing.expect(r == 26); try std.testing.expect(r == 26);
r = try b.decode_index('0'); r = try decode_index('0');
try std.testing.expect(r == 52); try std.testing.expect(r == 52);
r = try b.decode_index('/'); r = try decode_index('/');
try std.testing.expect(r == 63); try std.testing.expect(r == 63);
r = try b.decode_index('='); r = try decode_index('=');
try std.testing.expect(r == 64); try std.testing.expect(r == 64);
} }

View File

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const stdout = std.io.getStdOut().writer(); const stdout = std.io.getStdOut().writer();
const b64 = @import("base64.zig"); const Base64 = @import("base64.zig");
const usageText = const usageText =
\\Usage: baze64 {-d/-D} input \\Usage: baze64 {-d/-D} input
@ -16,22 +16,34 @@ pub fn main() !void {
var args = try std.process.argsWithAllocator(allocator); var args = try std.process.argsWithAllocator(allocator);
defer args.deinit(); defer args.deinit();
var b = b64.Base64.init; var decodeSwitch = false;
var input: []const u8 = undefined;
// Skip arg[0] // Skip arg[0]
_ = args.skip(); _ = args.skip();
var a = args.next(); while (true) {
if (a) |arg| { const argVal = args.next();
var arg: []const u8 = undefined;
if (argVal) |a| {
arg = a;
} else {
break;
}
if (std.mem.eql(u8, arg, "-d") or std.mem.eql(u8, arg, "-D")) { if (std.mem.eql(u8, arg, "-d") or std.mem.eql(u8, arg, "-D")) {
a = args.next(); decodeSwitch = true;
const decoded = try b.decode(allocator, arg); } else {
input = arg;
break;
}
}
if (decodeSwitch) {
const decoded = try Base64.decode(allocator, input);
try stdout.print("{s}", .{decoded}); try stdout.print("{s}", .{decoded});
} else { } else {
const encoded = try b.decode(allocator, arg); const encoded = try Base64.encode(allocator, input);
try stdout.print("{s}", .{encoded}); try stdout.print("{s}", .{encoded});
} }
} else {
try stdout.print("{s}", .{usageText});
}
} }