forked from
karlseguin.tngl.sh/http.zig
An HTTP/1.1 server for zig
1const std = @import("std");
2
3const mem = std.mem;
4const Allocator = std.mem.Allocator;
5
6// Similar to KeyValue with two important differences
7// 1 - We don't need to normalize (i.e. lowercase) the names, because they're
8// statically defined in code, and presumably, if the param is called "id"
9// then the developer will also fetch it as "id"
10// 2 - This is populated from Router, and the way router works is that it knows
11// the values before it knows the names. The addValue and addNames
12// methods reflect how Router uses this.
13pub const Params = struct {
14 len: usize,
15 names: [][]const u8,
16 values: [][]const u8,
17
18 pub fn init(allocator: Allocator, max: usize) !Params {
19 const allocation = try allocator.alloc([]const u8, 2 * max);
20 return .{
21 .len = 0,
22 .names = allocation[0..max],
23 .values = allocation[max..],
24 };
25 }
26
27 pub fn deinit(self: *Params, allocator: Allocator) void {
28 allocator.free(self.names.ptr[0 .. 2 * self.names.len]);
29 }
30
31 pub fn addValue(self: *Params, value: []const u8) void {
32 if (self.len == self.names.len) {
33 return;
34 }
35 self.values[self.len] = value;
36 self.len += 1;
37 }
38
39 // It should be impossible for names.len != self.len at this point, but it's
40 // a bit dangerous to assume that since self.names is re-used between requests
41 // and we don't want to leak anything, so I think enforcing a len of names.len
42 // is safer, since names is generally statically defined based on routes setup.
43 //
44 // `noalias names` as `names` being a pointer inside self.names doesn't make sense,
45 // and memcpy needs this guarantee anyways.
46 pub fn addNames(self: *Params, noalias names: [][]const u8) void {
47 std.debug.assert(names.len == self.len);
48 @memcpy(self.names[0..self.len], names);
49 }
50
51 pub fn get(self: *const Params, needle: []const u8) ?[]const u8 {
52 const names = self.names[0..self.len];
53 for (names, 0..) |name, i| {
54 if (mem.eql(u8, name, needle)) {
55 return self.values[i];
56 }
57 }
58
59 return null;
60 }
61
62 pub fn reset(self: *Params) void {
63 self.len = 0;
64 }
65};
66
67const t = @import("t.zig");
68test "params: get" {
69 const allocator = t.allocator;
70 var params = try Params.init(allocator, 10);
71 var names = [_][]const u8{ "over", "duncan" };
72 params.addValue("9000");
73 params.addValue("idaho");
74 params.addNames(names[0..]);
75
76 try t.expectEqual("9000", params.get("over").?);
77 try t.expectEqual("idaho", params.get("duncan").?);
78
79 params.reset();
80 try t.expectEqual(null, params.get("over"));
81 try t.expectEqual(null, params.get("duncan"));
82
83 params.addValue("!9000!");
84 params.addNames(names[0..1]);
85 try t.expectEqual("!9000!", params.get("over").?);
86
87 params.deinit(t.allocator);
88}