Skip to content

Commit

Permalink
feat(case_string_map): use new Pool-backed version
Browse files Browse the repository at this point in the history
  • Loading branch information
mookums committed Dec 21, 2024
1 parent 7b3e808 commit 3321020
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 67 deletions.
5 changes: 3 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
.minimum_zig_version = "0.13.0",
.dependencies = .{
.tardy = .{
.url = "git+https://github.com/mookums/tardy?ref=v0.2.0#543e6b01cba3caa691960a4a5a54d3419969f2d8",
.hash = "12202bc544928f0bb67ab4a30d3ff6d54c9d62643296c8d04303762b477b71fd002d",
//.url = "git+https://github.com/mookums/tardy?ref=v0.2.1#d6452871e1eabf802c2f2423b87932edd3645e94",
//.hash = "1220ceafa7dec628f8d4ac02e2f4484b79f3a5bd34ad3c0c7333604a10e5cb807f6d",
.path = "../tardy",
},
.bearssl = .{
.url = "git+https://github.com/mookums/bearssl-zig#37a96eee56fe2543579bbc6da148ca886f3dd32b",
Expand Down
91 changes: 74 additions & 17 deletions src/core/case_string_map.zig
Original file line number Diff line number Diff line change
@@ -1,35 +1,92 @@
const std = @import("std");
const assert = std.debug.assert;

const Pool = @import("tardy").Pool;

pub fn CaseStringMap(comptime T: type) type {
return std.ArrayHashMapUnmanaged([]const u8, T, struct {
pub fn hash(_: @This(), input: []const u8) u32 {
var h: u32 = 0;
for (input) |byte| {
h = h *% 31 +% std.ascii.toLower(byte);
return struct {
const Self = @This();
const InnerPool = Pool(Entry);
const PoolIterator = InnerPool.Iterator;

const Entry = struct {
hash: u32,
key: []const u8,
data: T,
};

pool: InnerPool,

pub fn init(allocator: std.mem.Allocator, size: usize) !Self {
const pool = try Pool(Entry).init(allocator, size, null, null);
return .{ .pool = pool };
}

pub fn deinit(self: *Self) void {
self.pool.deinit(null, null);
}

pub fn put(self: *Self, name: []const u8, data: T) !void {
const name_hash = hash(name);
const entry = try self.pool.borrow_hint(@intCast(name_hash));
entry.item.* = .{ .key = name, .hash = name_hash, .data = data };
}

pub fn put_assume_capacity(self: *Self, name: []const u8, data: T) void {
assert(self.pool.clean() > 0);
const name_hash = hash(name);
const entry = self.pool.borrow_hint(@intCast(name_hash)) catch unreachable;
entry.item.* = .{ .key = name, .hash = name_hash, .data = data };
}

pub fn get(self: *Self, name: []const u8) ?T {
const name_hash = hash(name);

var iter = self.pool.iterator();
while (iter.next()) |entry| {
if (entry.hash == name_hash) {
return entry.data;
}
}
return h;

return null;
}

pub fn iterator(self: *Self) PoolIterator {
return self.pool.iterator();
}

pub fn num_clean(self: *const Self) usize {
return self.pool.clean();
}

pub fn dirty(self: *const Self) usize {
return self.pool.dirty.count();
}

pub fn clear(self: *Self) void {
// unset all of the dirty bits, effectively clearing it.
self.pool.dirty.unsetAll();
}

pub fn eql(_: @This(), first: []const u8, second: []const u8, _: usize) bool {
if (first.len != second.len) return false;
for (first, second) |a, b| {
if (std.ascii.toLower(a) != std.ascii.toLower(b)) return false;
fn hash(name: []const u8) u32 {
var h = std.hash.Fnv1a_32.init();
for (name) |byte| {
h.update(&.{std.ascii.toLower(byte)});
}
return true;
return h.final();
}
}, true);
};
}

const testing = std.testing;

test "CaseStringMap: Add Stuff" {
var csm = CaseStringMap([]const u8){};
defer csm.deinit(testing.allocator);
try csm.ensureUnusedCapacity(testing.allocator, 2);
var csm = try CaseStringMap([]const u8).init(testing.allocator, 2);
defer csm.deinit();

csm.putAssumeCapacity("Content-Length", "100");
csm.putAssumeCapacity("Host", "localhost:9999");
try csm.put("Content-Length", "100");
csm.put_assume_capacity("Host", "localhost:9999");

const content_length = csm.get("content-length");
try testing.expect(content_length != null);
Expand Down
6 changes: 3 additions & 3 deletions src/core/zc_buffer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub const ZeroCopyBuffer = struct {
self.allocator.free(self.ptr[0..self.capacity]);
}

pub fn as_slice(self: *ZeroCopyBuffer) []u8 {
pub fn as_slice(self: *const ZeroCopyBuffer) []u8 {
return self.ptr[0..self.len];
}

Expand All @@ -30,7 +30,7 @@ pub const ZeroCopyBuffer = struct {
end: ?usize = null,
};

pub fn subslice(self: *ZeroCopyBuffer, options: SubsliceOptions) []u8 {
pub fn subslice(self: *const ZeroCopyBuffer, options: SubsliceOptions) []u8 {
const start: usize = options.start orelse 0;
const end: usize = options.end orelse self.len;
assert(start <= end);
Expand Down Expand Up @@ -68,7 +68,7 @@ pub const ZeroCopyBuffer = struct {
}
}

pub fn get_write_area_assume_space(self: *ZeroCopyBuffer, size: usize) []u8 {
pub fn get_write_area_assume_space(self: *const ZeroCopyBuffer, size: usize) []u8 {
assert(self.capacity - self.len >= size);
return self.ptr[self.len .. self.len + size];
}
Expand Down
5 changes: 2 additions & 3 deletions src/http/provision.zig
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ pub const Provision = struct {
provision.captures = ctx.allocator.alloc(Capture, config.capture_count_max) catch {
@panic("attempting to statically allocate more memory than available. (Captures)");
};
provision.queries = QueryMap{};
provision.queries.ensureUnusedCapacity(ctx.allocator, config.query_count_max) catch {
provision.queries = QueryMap.init(ctx.allocator, config.query_count_max) catch {
@panic("attempting to statically allocate more memory than available. (QueryMap)");
};
provision.request = Request.init(ctx.allocator, config.header_count_max) catch {
Expand All @@ -68,7 +67,7 @@ pub const Provision = struct {
provision.arena.deinit();
provision.request.deinit();
provision.response.deinit();
provision.queries.deinit(allocator);
provision.queries.deinit();
allocator.free(provision.captures);
}
}
Expand Down
14 changes: 5 additions & 9 deletions src/http/request.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,23 @@ pub const Request = struct {

/// This is for constructing a Request.
pub fn init(allocator: std.mem.Allocator, header_count_max: usize) !Request {
var headers = Headers{};
try headers.ensureUnusedCapacity(allocator, header_count_max);
const headers = try Headers.init(allocator, header_count_max);

return Request{
.allocator = allocator,
.headers = headers,
.method = null,
.uri = null,
.body = null,
};
}

pub fn deinit(self: *Request) void {
self.headers.deinit(self.allocator);
self.headers.deinit();
}

pub fn clear(self: *Request) void {
self.method = null;
self.uri = null;
self.body = null;
self.headers.clearRetainingCapacity();
self.headers.clear();
}

const RequestParseOptions = struct {
Expand Down Expand Up @@ -88,8 +84,8 @@ pub const Request = struct {
const value = std.mem.trimLeft(u8, header_iter.rest(), &.{' '});
if (value.len == 0) return HTTPError.MalformedRequest;

if (self.headers.count() >= self.headers.capacity() / 2) return HTTPError.TooManyHeaders;
self.headers.putAssumeCapacity(key, value);
if (self.headers.num_clean() == 0) return HTTPError.TooManyHeaders;
self.headers.put_assume_capacity(key, value);
}
}
}
Expand Down
13 changes: 6 additions & 7 deletions src/http/response.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ pub const Response = struct {
headers: Headers,

pub fn init(allocator: std.mem.Allocator, header_count_max: usize) !Response {
var headers = Headers{};
try headers.ensureUnusedCapacity(allocator, header_count_max);
const headers = try Headers.init(allocator, header_count_max);

return Response{
.allocator = allocator,
Expand All @@ -24,7 +23,7 @@ pub const Response = struct {
}

pub fn deinit(self: *Response) void {
self.headers.deinit(self.allocator);
self.headers.deinit();
}

pub fn clear(self: *Response) void {
Expand Down Expand Up @@ -77,12 +76,12 @@ pub const Response = struct {
// Headers
var iter = self.headers.iterator();
while (iter.next()) |entry| {
std.mem.copyForwards(u8, buffer[index..], entry.key_ptr.*);
index += entry.key_ptr.len;
std.mem.copyForwards(u8, buffer[index..], entry.key);
index += entry.key.len;
std.mem.copyForwards(u8, buffer[index..], ": ");
index += 2;
std.mem.copyForwards(u8, buffer[index..], entry.value_ptr.*);
index += entry.value_ptr.len;
std.mem.copyForwards(u8, buffer[index..], entry.data);
index += entry.data.len;
std.mem.copyForwards(u8, buffer[index..], "\r\n");
index += 2;
}
Expand Down
2 changes: 1 addition & 1 deletion src/http/router.zig
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn Router(comptime Server: type, comptime AppState: type) type {

pub fn get_route_from_host(self: Self, path: []const u8, captures: []Capture, queries: *QueryMap) !FoundRoute {
return try self.routes.get_route(path, captures, queries) orelse {
queries.clearRetainingCapacity();
queries.clear();
return FoundRoute{ .route = self.not_found_route, .captures = captures[0..0], .queries = queries };
};
}
Expand Down
27 changes: 12 additions & 15 deletions src/http/router/routing_trie.zig
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ pub fn RoutingTrie(comptime Server: type, comptime AppState: type) type {
) !?FoundRoute {
var capture_idx: usize = 0;

queries.clearRetainingCapacity();
queries.clear();
const query_pos = std.mem.indexOfScalar(u8, path, '?');
var iter = std.mem.tokenizeScalar(u8, path[0..(query_pos orelse path.len)], '/');

Expand Down Expand Up @@ -262,7 +262,7 @@ pub fn RoutingTrie(comptime Server: type, comptime AppState: type) type {
var query_iter = std.mem.tokenizeScalar(u8, path[pos + 1 ..], '&');

while (query_iter.next()) |chunk| {
if (queries.count() >= queries.capacity() / 2) return null;
if (queries.pool.clean() == 0) return null;

const field_idx = std.mem.indexOfScalar(u8, chunk, '=') orelse break;
if (chunk.len < field_idx + 1) break;
Expand All @@ -272,7 +272,7 @@ pub fn RoutingTrie(comptime Server: type, comptime AppState: type) type {

assert(std.mem.indexOfScalar(u8, key, '=') == null);
assert(std.mem.indexOfScalar(u8, value, '=') == null);
queries.putAssumeCapacity(key, value);
queries.put_assume_capacity(key, value);
}
}
}
Expand Down Expand Up @@ -372,9 +372,8 @@ test "Routing with Paths" {
Route.init("/item/list"),
});

var q = try QueryMap.init(testing.allocator, &[_][]const u8{}, &[_][]const u8{});
try q.ensureTotalCapacity(testing.allocator, 8);
defer q.deinit(testing.allocator);
var q = try QueryMap.init(testing.allocator, 8);
defer q.deinit();

var captures: [8]Capture = [_]Capture{undefined} ** 8;

Expand Down Expand Up @@ -405,9 +404,8 @@ test "Routing with Remaining" {
Route.init("/item/%i/price/%f"),
});

var q = try QueryMap.init(testing.allocator, &[_][]const u8{}, &[_][]const u8{});
try q.ensureTotalCapacity(testing.allocator, 8);
defer q.deinit(testing.allocator);
var q = try QueryMap.init(testing.allocator, 8);
defer q.deinit();

var captures: [8]Capture = [_]Capture{undefined} ** 8;

Expand Down Expand Up @@ -448,9 +446,8 @@ test "Routing with Queries" {
Route.init("/item/%i/price/%f"),
});

var q = try QueryMap.init(testing.allocator, &[_][]const u8{}, &[_][]const u8{});
try q.ensureTotalCapacity(testing.allocator, 8);
defer q.deinit(testing.allocator);
var q = try QueryMap.init(testing.allocator, 8);
defer q.deinit();

var captures: [8]Capture = [_]Capture{undefined} ** 8;

Expand All @@ -460,7 +457,7 @@ test "Routing with Queries" {
const captured = (try s.get_route("/item/name/HELLO?name=muki&food=waffle", captures[0..], &q)).?;
try testing.expectEqual(Route.init("/item/name/%r"), captured.route);
try testing.expectEqualStrings("HELLO", captured.captures[0].remaining);
try testing.expectEqual(2, q.count());
try testing.expectEqual(2, q.dirty());
try testing.expectEqualStrings("muki", q.get("name").?);
try testing.expectEqualStrings("waffle", q.get("food").?);
}
Expand All @@ -470,7 +467,7 @@ test "Routing with Queries" {
const captured = (try s.get_route("/item/2112.22121/price_float?", captures[0..], &q)).?;
try testing.expectEqual(Route.init("/item/%f/price_float"), captured.route);
try testing.expectEqual(2112.22121, captured.captures[0].float);
try testing.expectEqual(0, q.count());
try testing.expectEqual(0, q.dirty());
}

{
Expand All @@ -479,7 +476,7 @@ test "Routing with Queries" {
try testing.expectEqual(Route.init("/item/%i/price/%f"), captured.route);
try testing.expectEqual(100, captured.captures[0].signed);
try testing.expectEqual(283.21, captured.captures[1].float);
try testing.expectEqual(0, q.count());
try testing.expectEqual(0, q.dirty());
}

{
Expand Down
16 changes: 6 additions & 10 deletions src/http/server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub inline fn raw_respond(p: *Provision) !RecvStatus {

const body = p.response.body orelse "";
const header_buffer = try p.response.headers_into_buffer(p.buffer, @intCast(body.len));
p.response.headers.clearRetainingCapacity();
p.response.headers.clear();
const pseudo = Pseudoslice.init(header_buffer, body, p.buffer);
return .{ .send = pseudo };
}
Expand Down Expand Up @@ -125,12 +125,8 @@ pub const ServerConfig = struct {
/// Size of the buffer (in bytes) used for
/// interacting with the socket.
///
/// Default: 4 KB.
socket_buffer_bytes: u32 = 1024 * 4,
/// Maximum length of a Header Key
///
/// Default: 64
header_key_length_max: u16 = 64,
/// Default: 1 KB.
socket_buffer_bytes: u32 = 1024,
/// Maximum number of Headers in a Request/Response
///
/// Default: 32
Expand Down Expand Up @@ -740,7 +736,7 @@ pub fn Server(comptime security: Security, comptime AppState: type) type {
}.send_then_inner;
}

pub inline fn serve(self: *Self, router: *const Router, rt: *Runtime) !void {
pub fn serve(self: *Self, router: *const Router, rt: *Runtime) !void {
if (self.addr == null) return error.ServerNotBinded;
const addr = self.addr.?;
try rt.storage.store_alloc("__zzz_is_unix", addr.any.family == std.posix.AF.UNIX);
Expand Down Expand Up @@ -786,7 +782,7 @@ pub fn Server(comptime security: Security, comptime AppState: type) type {
try rt.net.accept(socket, accept_task, socket);
}

pub inline fn clean(rt: *Runtime) !void {
pub fn clean(rt: *Runtime) !void {
// clean up socket.
const server_socket = rt.storage.get("__zzz_server_socket", std.posix.socket_t);
std.posix.close(server_socket);
Expand Down Expand Up @@ -896,7 +892,7 @@ pub fn Server(comptime security: Security, comptime AppState: type) type {
break :route;
};

p.response.headers.putAssumeCapacity("Allow", allowed);
p.response.headers.put_assume_capacity("Allow", allowed);
break :route;
}
}
Expand Down

0 comments on commit 3321020

Please sign in to comment.