mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
pico
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
||||
# [submodule "src/deps/zig-clap"]
|
||||
# path = src/deps/zig-clap
|
||||
# url = https://github.com/Hejsil/zig-clap
|
||||
[submodule "src/deps/picohttpparser"]
|
||||
path = src/deps/picohttpparser
|
||||
url = https://github.com/h2o/picohttpparser/
|
||||
|
||||
11
build.zig
11
build.zig
@@ -1,5 +1,15 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn addPicoHTTP(step: *std.build.LibExeObjStep, comptime dir: []const u8) void {
|
||||
step.addCSourceFile(dir ++ "/picohttpparser/picohttpparser.c", &[_][]const u8{});
|
||||
step.addIncludeDir(dir ++ "/picohttpparser");
|
||||
|
||||
step.addPackage(.{
|
||||
.name = "picohttp",
|
||||
.path = dir ++ "/picohttp.zig",
|
||||
});
|
||||
}
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
// Standard target options allows the person running `zig build` to choose
|
||||
// what target to build for. Here we do not override the defaults, which
|
||||
@@ -97,6 +107,7 @@ pub fn build(b: *std.build.Builder) void {
|
||||
|
||||
if (!target.getCpuArch().isWasm()) {
|
||||
exe.addLibPath("/usr/local/lib");
|
||||
addPicoHTTP(exe, "src/deps");
|
||||
}
|
||||
|
||||
exe.install();
|
||||
|
||||
@@ -283,9 +283,9 @@ pub const Cli = struct {
|
||||
var log = logger.Log.init(allocator);
|
||||
var panicker = MainPanicHandler.init(&log);
|
||||
MainPanicHandler.Singleton = &panicker;
|
||||
try Server.start(allocator);
|
||||
|
||||
const args = try Arguments.parse(alloc.static, stdout, stderr);
|
||||
var args = try Arguments.parse(alloc.static, stdout, stderr);
|
||||
try Server.start(allocator, &args);
|
||||
var result: options.TransformResult = undefined;
|
||||
switch (args.resolve orelse Api.ResolveMode.dev) {
|
||||
Api.ResolveMode.disable => {
|
||||
|
||||
261
src/deps/picohttp.zig
Normal file
261
src/deps/picohttp.zig
Normal file
@@ -0,0 +1,261 @@
|
||||
const std = @import("std");
|
||||
const c = @cImport(@cInclude("picohttpparser.h"));
|
||||
const ExactSizeMatcher = @import("../exact_size_matcher.zig").ExactSizeMatcher;
|
||||
const Match = ExactSizeMatcher(2);
|
||||
|
||||
const fmt = std.fmt;
|
||||
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub fn addTo(step: *std.build.LibExeObjStep, comptime dir: []const u8) void {
|
||||
step.addCSourceFile(dir ++ "/lib/picohttpparser.c", &[_][]const u8{});
|
||||
step.addIncludeDir(dir ++ "/lib");
|
||||
|
||||
step.addPackage(.{
|
||||
.name = "picohttp",
|
||||
.path = dir ++ "/picohttp.zig",
|
||||
});
|
||||
}
|
||||
|
||||
pub const Header = struct {
|
||||
name: []const u8,
|
||||
value: []const u8,
|
||||
|
||||
pub fn isMultiline(self: Header) bool {
|
||||
return @ptrToInt(self.name.ptr) == 0;
|
||||
}
|
||||
|
||||
pub fn format(self: Header, comptime layout: []const u8, opts: fmt.FormatOptions, writer: anytype) !void {
|
||||
if (self.isMultiline()) {
|
||||
try fmt.format(writer, "{s}", .{self.value});
|
||||
} else {
|
||||
try fmt.format(writer, "{s}: {s}", .{ self.name, self.value });
|
||||
}
|
||||
}
|
||||
|
||||
comptime {
|
||||
assert(@sizeOf(Header) == @sizeOf(c.phr_header));
|
||||
assert(@alignOf(Header) == @alignOf(c.phr_header));
|
||||
}
|
||||
};
|
||||
|
||||
pub const Request = struct {
|
||||
method_: []const u8,
|
||||
method: Method,
|
||||
path: []const u8,
|
||||
minor_version: usize,
|
||||
headers: []const Header,
|
||||
|
||||
pub const Method = enum {
|
||||
GET,
|
||||
HEAD,
|
||||
PATCH,
|
||||
PUT,
|
||||
POST,
|
||||
OPTIONS,
|
||||
CONNECT,
|
||||
TRACE,
|
||||
|
||||
pub fn which(str: []const u8) ?Method {
|
||||
if (str.len < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (Match.match(str[0..2])) {
|
||||
Match.case("GE"), Match.case("ge") => {
|
||||
return .GET;
|
||||
},
|
||||
Match.case("HE"), Match.case("he") => {
|
||||
return .HEAD;
|
||||
},
|
||||
Match.case("PA"), Match.case("pa") => {
|
||||
return .PATCH;
|
||||
},
|
||||
Match.case("PO"), Match.case("po") => {
|
||||
return .POST;
|
||||
},
|
||||
Match.case("PU"), Match.case("pu") => {
|
||||
return .PUT;
|
||||
},
|
||||
Match.case("OP"), Match.case("op") => {
|
||||
return .OPTIONS;
|
||||
},
|
||||
Match.case("CO"), Match.case("co") => {
|
||||
return .CONNECT;
|
||||
},
|
||||
Match.case("TR"), Match.case("tr") => {
|
||||
return .TRACE;
|
||||
},
|
||||
else => {
|
||||
return null;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn parse(buf: []const u8, src: []Header) !Request {
|
||||
var method: []const u8 = undefined;
|
||||
var path: []const u8 = undefined;
|
||||
var minor_version: c_int = undefined;
|
||||
var num_headers: usize = src.len;
|
||||
|
||||
const rc = c.phr_parse_request(
|
||||
buf.ptr,
|
||||
buf.len,
|
||||
@ptrCast([*c][*c]const u8, &method.ptr),
|
||||
&method.len,
|
||||
@ptrCast([*c][*c]const u8, &path.ptr),
|
||||
&path.len,
|
||||
&minor_version,
|
||||
@ptrCast([*c]c.phr_header, src.ptr),
|
||||
&num_headers,
|
||||
0,
|
||||
);
|
||||
|
||||
return switch (rc) {
|
||||
-1 => error.BadRequest,
|
||||
-2 => error.ShortRead,
|
||||
else => |bytes_read| Request{
|
||||
.method_ = method,
|
||||
.method = Request.Method.which(method) orelse return error.InvalidMethod,
|
||||
.path = path,
|
||||
.minor_version = @intCast(usize, minor_version),
|
||||
.headers = src[0..num_headers],
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test "pico_http: parse request" {
|
||||
const REQ = "GET /wp-content/uploads/2010/03/hello-kitty-darth-vader-pink.jpg HTTP/1.1\r\n" ++
|
||||
"Host: www.kittyhell.com\r\n" ++
|
||||
"User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 " ++
|
||||
"Pathtraq/0.9\r\n" ++
|
||||
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" ++
|
||||
"Accept-Language: ja,en-us;q=0.7,en;q=0.3\r\n" ++
|
||||
"Accept-Encoding: gzip,deflate\r\n" ++
|
||||
"Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7\r\n" ++
|
||||
"Keep-Alive: 115\r\n" ++
|
||||
"Connection: keep-alive\r\n" ++
|
||||
"TestMultiline: Hello world\r\n" ++
|
||||
" This is a second line in the header!\r\n" ++
|
||||
"Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; " ++
|
||||
"__utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; " ++
|
||||
"__utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral\r\n" ++
|
||||
"\r\n";
|
||||
|
||||
var headers: [32]Header = undefined;
|
||||
|
||||
const req = try Request.parse(REQ, &headers);
|
||||
|
||||
std.debug.print("Method: {s}\n", .{req.method});
|
||||
std.debug.print("Path: {s}\n", .{req.path});
|
||||
std.debug.print("Minor Version: {}\n", .{req.minor_version});
|
||||
|
||||
for (req.headers) |header| {
|
||||
std.debug.print("{}\n", .{header});
|
||||
}
|
||||
}
|
||||
|
||||
pub const Response = struct {
|
||||
minor_version: usize,
|
||||
status_code: usize,
|
||||
status: []const u8,
|
||||
headers: []const Header,
|
||||
|
||||
pub fn parse(buf: []const u8, src: []Header) !Response {
|
||||
var minor_version: c_int = undefined;
|
||||
var status_code: c_int = undefined;
|
||||
var status: []const u8 = undefined;
|
||||
var num_headers: usize = src.len;
|
||||
|
||||
const rc = c.phr_parse_response(
|
||||
buf.ptr,
|
||||
buf.len,
|
||||
&minor_version,
|
||||
&status_code,
|
||||
@ptrCast([*c][*c]const u8, &status.ptr),
|
||||
&status.len,
|
||||
@ptrCast([*c]c.phr_header, src.ptr),
|
||||
&num_headers,
|
||||
0,
|
||||
);
|
||||
|
||||
return switch (rc) {
|
||||
-1 => error.BadResponse,
|
||||
-2 => error.ShortRead,
|
||||
else => |bytes_read| Response{
|
||||
.minor_version = @intCast(usize, minor_version),
|
||||
.status_code = @intCast(usize, status_code),
|
||||
.status = status,
|
||||
.headers = src[0..num_headers],
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test "pico_http: parse response" {
|
||||
const RES = "HTTP/1.1 200 OK\r\n" ++
|
||||
"Date: Mon, 22 Mar 2021 08:15:54 GMT\r\n" ++
|
||||
"Content-Type: text/html; charset=utf-8\r\n" ++
|
||||
"Content-Length: 9593\r\n" ++
|
||||
"Connection: keep-alive\r\n" ++
|
||||
"Server: gunicorn/19.9.0\r\n" ++
|
||||
"Access-Control-Allow-Origin: *\r\n" ++
|
||||
"Access-Control-Allow-Credentials: true\r\n" ++
|
||||
"\r\n";
|
||||
|
||||
var headers: [32]Header = undefined;
|
||||
|
||||
const res = try Response.parse(RES, &headers);
|
||||
|
||||
std.debug.print("Minor Version: {}\n", .{res.minor_version});
|
||||
std.debug.print("Status Code: {}\n", .{res.status_code});
|
||||
std.debug.print("Status: {s}\n", .{res.status});
|
||||
|
||||
for (res.headers) |header| {
|
||||
std.debug.print("{}\n", .{header});
|
||||
}
|
||||
}
|
||||
|
||||
pub const Headers = struct {
|
||||
headers: []const Header,
|
||||
|
||||
pub fn parse(buf: []const u8, src: []Header) !Headers {
|
||||
var num_headers: usize = src.len;
|
||||
|
||||
const rc = c.phr_parse_headers(
|
||||
buf.ptr,
|
||||
buf.len,
|
||||
@ptrCast([*c]c.phr_header, src.ptr),
|
||||
@ptrCast([*c]usize, &num_headers),
|
||||
0,
|
||||
);
|
||||
|
||||
return switch (rc) {
|
||||
-1 => error.BadHeaders,
|
||||
-2 => error.ShortRead,
|
||||
else => |bytes_read| Headers{
|
||||
.headers = src[0..num_headers],
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test "pico_http: parse headers" {
|
||||
const HEADERS = "Date: Mon, 22 Mar 2021 08:15:54 GMT\r\n" ++
|
||||
"Content-Type: text/html; charset=utf-8\r\n" ++
|
||||
"Content-Length: 9593\r\n" ++
|
||||
"Connection: keep-alive\r\n" ++
|
||||
"Server: gunicorn/19.9.0\r\n" ++
|
||||
"Access-Control-Allow-Origin: *\r\n" ++
|
||||
"Access-Control-Allow-Credentials: true\r\n" ++
|
||||
"\r\n";
|
||||
|
||||
var headers: [32]Header = undefined;
|
||||
|
||||
const result = try Headers.parse(HEADERS, &headers);
|
||||
for (result.headers) |header| {
|
||||
std.debug.print("{}\n", .{header});
|
||||
}
|
||||
}
|
||||
1
src/deps/picohttpparser
Submodule
1
src/deps/picohttpparser
Submodule
Submodule src/deps/picohttpparser added at 066d2b1e9a
46
src/exact_size_matcher.zig
Normal file
46
src/exact_size_matcher.zig
Normal file
@@ -0,0 +1,46 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn ExactSizeMatcher(comptime max_bytes: usize) type {
|
||||
const T = std.meta.Int(
|
||||
.unsigned,
|
||||
max_bytes * 8,
|
||||
);
|
||||
|
||||
return struct {
|
||||
pub fn match(str: anytype) T {
|
||||
return hash(str) orelse std.math.maxInt(T);
|
||||
}
|
||||
|
||||
pub fn case(comptime str: []const u8) T {
|
||||
return hash(str) orelse std.math.maxInt(T);
|
||||
}
|
||||
|
||||
pub fn hash(str: anytype) ?T {
|
||||
if (str.len > max_bytes) return null;
|
||||
var tmp = [_]u8{0} ** max_bytes;
|
||||
std.mem.copy(u8, &tmp, str[0..str.len]);
|
||||
return std.mem.readIntNative(T, &tmp);
|
||||
}
|
||||
|
||||
pub fn hashUnsafe(str: anytype) T {
|
||||
var tmp = [_]u8{0} ** max_bytes;
|
||||
std.mem.copy(u8, &tmp, str[0..str.len]);
|
||||
return std.mem.readIntNative(T, &tmp);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const eight = ExactSizeMatcher(8);
|
||||
|
||||
test "ExactSizeMatcher 5 letter" {
|
||||
const word = "yield";
|
||||
expect(eight.match(word) == eight.case("yield"));
|
||||
expect(eight.match(word) != eight.case("yields"));
|
||||
}
|
||||
|
||||
test "ExactSizeMatcher 4 letter" {
|
||||
const Four = ExactSizeMatcher(4);
|
||||
const word = "from";
|
||||
expect(Four.match(word) == Four.case("from"));
|
||||
expect(Four.match(word) != Four.case("fro"));
|
||||
}
|
||||
207
src/http.zig
207
src/http.zig
@@ -1,119 +1,112 @@
|
||||
// const c = @import("./c.zig");
|
||||
const std = @import("std");
|
||||
usingnamespace @import("global.zig");
|
||||
const Address = std.net.Address;
|
||||
const routez = @import("routez");
|
||||
const Request = routez.Request;
|
||||
const Response = routez.Response;
|
||||
const Api = @import("./api/schema.zig").Api;
|
||||
|
||||
const tcp = std.x.net.tcp;
|
||||
const ip = std.x.net.ip;
|
||||
|
||||
const IPv4 = std.x.os.IPv4;
|
||||
const IPv6 = std.x.os.IPv6;
|
||||
const Socket = std.x.os.Socket;
|
||||
const os = std.os;
|
||||
|
||||
const picohttp = @import("picohttp");
|
||||
const Header = picohttp.Header;
|
||||
const Request = picohttp.Request;
|
||||
const Response = picohttp.Response;
|
||||
const Headers = picohttp.Headers;
|
||||
|
||||
pub const Server = struct {
|
||||
pub fn start(allocator: *std.mem.Allocator) !void {
|
||||
var server = routez.Server.init(
|
||||
allocator,
|
||||
.{},
|
||||
.{
|
||||
routez.all("/", indexHandler),
|
||||
routez.get("/about", aboutHandler),
|
||||
routez.get("/about/more", aboutHandler2),
|
||||
routez.get("/post/{post_num}/?", postHandler),
|
||||
routez.static("./", "/static"),
|
||||
routez.all("/counter", counterHandler),
|
||||
},
|
||||
);
|
||||
var addr = Address.parseIp("127.0.0.1", 8080) catch unreachable;
|
||||
server.listen(addr) catch unreachable;
|
||||
options: *Api.TransformOptions,
|
||||
allocator: *std.mem.Allocator,
|
||||
|
||||
threadlocal var headers_buf: [100]picohttp.Header = undefined;
|
||||
|
||||
fn run(server: *Server) !void {
|
||||
const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC);
|
||||
defer listener.deinit();
|
||||
|
||||
listener.setReuseAddress(true) catch {};
|
||||
listener.setReusePort(true) catch {};
|
||||
listener.setFastOpen(true) catch {};
|
||||
// try listener.ack(true);
|
||||
|
||||
try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 9000));
|
||||
try listener.listen(128);
|
||||
|
||||
// try listener.set(true);
|
||||
|
||||
while (true) {
|
||||
var conn = try listener.accept(os.SOCK_CLOEXEC);
|
||||
server.handleConnection(&conn);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeStatus(server: *Server, comptime code: u9, conn: *tcp.Connection) !void {
|
||||
_ = try conn.client.write(std.fmt.comptimePrint("HTTP/1.1 {d}\r\n", .{code}), os.SOCK_CLOEXEC);
|
||||
}
|
||||
|
||||
pub fn sendError(server: *Server, request: *Request, conn: *tcp.Connection, code: u9, msg: string) !void {
|
||||
try server.writeStatus(code, connection);
|
||||
conn.deinit();
|
||||
}
|
||||
|
||||
pub fn handleRequest(server: *Server, request: *Request, conn: *tcp.Connection) !void {
|
||||
try server.writeStatus(200, conn);
|
||||
conn.deinit();
|
||||
// switch (request.method) {
|
||||
// .GET, .HEAD => {},
|
||||
// else => {},
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn handleConnection(server: *Server, conn: *tcp.Connection) void {
|
||||
errdefer conn.deinit();
|
||||
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
|
||||
var req_buf: [std.mem.page_size]u8 = undefined;
|
||||
var read_size = conn.client.read(&req_buf, os.SOCK_CLOEXEC) catch |err| {
|
||||
return;
|
||||
};
|
||||
var req = picohttp.Request.parse(req_buf[0..read_size], &headers_buf) catch |err| {
|
||||
Output.printError("ERR: {s}", .{@errorName(err)});
|
||||
|
||||
return;
|
||||
};
|
||||
server.handleRequest(&req, conn) catch |err| {
|
||||
Output.printError("FAIL [{s}] - {s}: {s}", .{ @errorName(err), @tagName(req.method), req.path });
|
||||
conn.deinit();
|
||||
return;
|
||||
};
|
||||
Output.print("[{s}] - {s}", .{ @tagName(req.method), req.path });
|
||||
}
|
||||
|
||||
pub fn start(allocator: *std.mem.Allocator, options: *Api.TransformOptions) !void {
|
||||
var server = Server{ .options = options, .allocator = allocator };
|
||||
|
||||
try server.run();
|
||||
}
|
||||
};
|
||||
|
||||
fn indexHandler(req: Request, res: Response) !void {
|
||||
try res.write("hi\n");
|
||||
}
|
||||
// fn indexHandler(req: Request, res: Response) !void {
|
||||
// try res.write("hi\n");
|
||||
// }
|
||||
|
||||
fn aboutHandler(req: Request, res: Response) !void {
|
||||
try res.write("Hello from about\n");
|
||||
}
|
||||
// fn aboutHandler(req: Request, res: Response) !void {
|
||||
// try res.write("Hello from about\n");
|
||||
// }
|
||||
|
||||
fn aboutHandler2(req: Request, res: Response) !void {
|
||||
try res.write("Hello from about2\n");
|
||||
}
|
||||
// fn aboutHandler2(req: Request, res: Response) !void {
|
||||
// try res.write("Hello from about2\n");
|
||||
// }
|
||||
|
||||
fn postHandler(req: Request, res: Response, args: *const struct {
|
||||
post_num: []const u8,
|
||||
}) !void {
|
||||
try res.print("Hello from post, post_num is {s}\n", .{args.post_num});
|
||||
}
|
||||
// fn postHandler(req: Request, res: Response, args: *const struct {
|
||||
// post_num: []const u8,
|
||||
// }) !void {
|
||||
// try res.print("Hello from post, post_num is {s}\n", .{args.post_num});
|
||||
// }
|
||||
|
||||
var counter = std.atomic.Int(usize).init(0);
|
||||
fn counterHandler(req: Request, res: Response) !void {
|
||||
try res.print("Page loaded {d} times\n", .{counter.fetchAdd(1)});
|
||||
}
|
||||
|
||||
// pub const Server = struct {
|
||||
// pub var server = std.mem.zeroes(c.struct_mg_callbacks);
|
||||
|
||||
// pub fn beginRequest(conn: ?*c.struct_mg_connection) callconv(.C) c_int {
|
||||
// return 0;
|
||||
// }
|
||||
// pub fn endRequest(conn: ?*const c.struct_mg_connection, status_code: c_int) callconv(.C) void {}
|
||||
// pub fn logMessage(conn: ?*const c.struct_mg_connection, msg: [*c]const u8) callconv(.C) c_int {
|
||||
// return 1;
|
||||
// }
|
||||
// pub fn logAccess(conn: ?*const c.struct_mg_connection, msg: [*c]const u8) callconv(.C) c_int {
|
||||
// return 1;
|
||||
// }
|
||||
// // pub fn initSsl(conn: ?*c_void, ?*c_void) callconv(.C) c_int
|
||||
// // pub fn initSslDomain(conn: [*c]const u8, ?*c_void, ?*c_void) callconv(.C) c_int
|
||||
// // pub fn externalSslCtx(ctx: [*c]?*c_void, ?*c_void) callconv(.C) c_int
|
||||
// // pub fn externalSslCtxDomain(ctx: [*c]const u8, [*c]?*c_void, ?*c_void) callconv(.C) c_int
|
||||
// // pub fn connectionClose(conn: ?*const c.struct_mg_connection) callconv(.C) void
|
||||
// // pub fn connectionClosed(conn: ?*const c.struct_mg_connection) callconv(.C) void
|
||||
// // pub fn initLua(conn: ?*const c.struct_mg_connection, ?*c_void, c_uint) callconv(.C) void
|
||||
// // pub fn exitLua(conn: ?*const c.struct_mg_connection, ?*c_void, c_uint) callconv(.C) void
|
||||
// pub fn httpError(conn: ?*c.struct_mg_connection, status: c_int, msg: [*c]const u8) callconv(.C) c_int {
|
||||
// return 0;
|
||||
// }
|
||||
// pub fn handleCodeRequest(conn: ?*c.struct_mg_connection, cbdata: ?*c_void) c_int {
|
||||
// var buf = "helloooo";
|
||||
// var buf_slice = buf[0.. :0];
|
||||
// // c.mg_write(conn, &buf_slice, buf_slice.len);
|
||||
// c.mg_send_http_ok(conn, "text/plain", buf_slice.len);
|
||||
// return 200;
|
||||
// }
|
||||
// pub fn initContext(ctx: *c.struct_mg_context) callconv(.C) void {
|
||||
// c.mg_set_request_handler(ctx, "/_src/", &handleCodeRequest, null);
|
||||
// }
|
||||
// pub fn exitContext(ctx: *c.struct_mg_context) callconv(.C) void {}
|
||||
// pub fn initThread(ctx: *c.struct_mg_context, thread_type: c_int) callconv(.C) ?*c_void {}
|
||||
// pub fn exitThread(ctx: *c.struct_mg_context, thread_type: c_int, user_ptr: ?*c_void) callconv(.C) void {}
|
||||
|
||||
// // pub fn initConnection(ctx: ?*const c.struct_mg_connection, [*c]?*c_void) callconv(.C) c_int {
|
||||
|
||||
// // }
|
||||
|
||||
// pub fn start() !void {
|
||||
// // server.
|
||||
// server.begin_request = &beginRequest;
|
||||
// server.end_request = &endRequest;
|
||||
// server.log_message = &logMessage;
|
||||
// server.log_access = &logAccess;
|
||||
// server.http_error = &httpError;
|
||||
// server.init_context = &initContext;
|
||||
// server.exit_context = &exitContext;
|
||||
// server.init_thread = &initThread;
|
||||
// server.exit_thread = &exitThread;
|
||||
// const val = c.mg_init_library(c.MG_FEATURES_COMPRESSION);
|
||||
// // callbacks.log_access
|
||||
// var opts = [_:null][*c]const u8{
|
||||
// "listening_ports",
|
||||
// "4086",
|
||||
// "request_timeout_ms",
|
||||
// "10000",
|
||||
// "error_log_file",
|
||||
// "error.log",
|
||||
// "enable_auth_domain_check",
|
||||
// "no",
|
||||
// };
|
||||
|
||||
// c.mg_start(&server, 0, opts);
|
||||
// }
|
||||
// };
|
||||
// var counter = std.atomic.Int(usize).init(0);
|
||||
// fn counterHandler(req: Request, res: Response) !void {
|
||||
// try res.print("Page loaded {d} times\n", .{counter.fetchAdd(1)});
|
||||
// }
|
||||
|
||||
@@ -360,47 +360,4 @@ test "sortDesc" {
|
||||
std.testing.expectEqualStrings(sorted_join, string_join);
|
||||
}
|
||||
|
||||
pub fn ExactSizeMatcher(comptime max_bytes: usize) type {
|
||||
const T = std.meta.Int(
|
||||
.unsigned,
|
||||
max_bytes * 8,
|
||||
);
|
||||
|
||||
return struct {
|
||||
pub fn match(str: anytype) T {
|
||||
return hash(str) orelse std.math.maxInt(T);
|
||||
}
|
||||
|
||||
pub fn case(comptime str: []const u8) T {
|
||||
return hash(str) orelse std.math.maxInt(T);
|
||||
}
|
||||
|
||||
fn hash(str: anytype) ?T {
|
||||
if (str.len > max_bytes) return null;
|
||||
var tmp = [_]u8{0} ** max_bytes;
|
||||
std.mem.copy(u8, &tmp, str[0..str.len]);
|
||||
return std.mem.readIntNative(T, &tmp);
|
||||
}
|
||||
|
||||
fn hashUnsafe(str: anytype) T {
|
||||
var tmp = [_]u8{0} ** max_bytes;
|
||||
std.mem.copy(u8, &tmp, str[0..str.len]);
|
||||
return std.mem.readIntNative(T, &tmp);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const eight = ExactSizeMatcher(8);
|
||||
|
||||
test "ExactSizeMatcher 5 letter" {
|
||||
const word = "yield";
|
||||
expect(eight.match(word) == eight.case("yield"));
|
||||
expect(eight.match(word) != eight.case("yields"));
|
||||
}
|
||||
|
||||
test "ExactSizeMatcher 4 letter" {
|
||||
const Four = ExactSizeMatcher(4);
|
||||
const word = "from";
|
||||
expect(Four.match(word) == Four.case("from"));
|
||||
expect(Four.match(word) != Four.case("fro"));
|
||||
}
|
||||
pub usingnamespace @import("exact_size_matcher.zig");
|
||||
|
||||
Reference in New Issue
Block a user