prefect server in zig
1const std = @import("std");
2
3/// base64 encode a byte slice
4pub fn base64Encode(alloc: std.mem.Allocator, data: []const u8) ![]const u8 {
5 const encoder = std.base64.standard.Encoder;
6 const size = encoder.calcSize(data.len);
7 const buf = try alloc.alloc(u8, size);
8 return encoder.encode(buf, data);
9}
10
11/// base64 decode a string
12pub fn base64Decode(alloc: std.mem.Allocator, encoded: []const u8) ![]const u8 {
13 const decoder = std.base64.standard.Decoder;
14 // upper bound: 3 bytes per 4 chars
15 const max_size = (encoded.len / 4 + 1) * 3;
16 const buf = try alloc.alloc(u8, max_size);
17 decoder.decode(buf, encoded) catch return error.InvalidBase64;
18 // calculate actual decoded length (may be less due to padding)
19 const actual_len = encoded.len / 4 * 3 - countPadding(encoded);
20 return buf[0..actual_len];
21}
22
23fn countPadding(encoded: []const u8) usize {
24 var padding: usize = 0;
25 if (encoded.len > 0 and encoded[encoded.len - 1] == '=') padding += 1;
26 if (encoded.len > 1 and encoded[encoded.len - 2] == '=') padding += 1;
27 return padding;
28}
29
30/// URL decode a percent-encoded string (handles %XX and + for space)
31pub fn urlDecode(alloc: std.mem.Allocator, encoded: []const u8) ![]const u8 {
32 var result = std.ArrayListUnmanaged(u8){};
33 var i: usize = 0;
34 while (i < encoded.len) {
35 if (encoded[i] == '%' and i + 2 < encoded.len) {
36 const hex = encoded[i + 1 .. i + 3];
37 const byte = std.fmt.parseInt(u8, hex, 16) catch {
38 try result.append(alloc, encoded[i]);
39 i += 1;
40 continue;
41 };
42 try result.append(alloc, byte);
43 i += 3;
44 } else if (encoded[i] == '+') {
45 try result.append(alloc, ' ');
46 i += 1;
47 } else {
48 try result.append(alloc, encoded[i]);
49 i += 1;
50 }
51 }
52 return result.toOwnedSlice(alloc);
53}