From c73fb2ed032ec53c62ae5d39a1c64a1c78444367 Mon Sep 17 00:00:00 2001 From: Ankit Patial Date: Mon, 19 Jan 2026 12:11:45 +0530 Subject: [PATCH] Fix memory leak in demo, document arena allocator usage --- README.md | 21 +++++++++++++++++++++ src/examples/demo/main.zig | 11 ++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ddfaf18..3cdfc2b 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ exe.root_module.addImport("pugz", pugz.module("pugz")); ## Usage +**Important:** Always use an arena allocator for rendering. The render function creates many small allocations that should be freed together. Using a general-purpose allocator without freeing will cause memory leaks. + ```zig const std = @import("std"); const pugz = @import("pugz"); @@ -54,6 +56,25 @@ pub fn main() !void { } ``` +### With http.zig + +When using with http.zig, use `res.arena` which is automatically freed after each response: + +```zig +fn handler(app: *App, _: *httpz.Request, res: *httpz.Response) !void { + const html = app.view.render(res.arena, "index", .{ + .title = "Hello", + }) catch |err| { + res.status = 500; + res.body = @errorName(err); + return; + }; + + res.content_type = .HTML; + res.body = html; +} +``` + ### Template String ```zig diff --git a/src/examples/demo/main.zig b/src/examples/demo/main.zig index d1d2cfd..ec97ca7 100644 --- a/src/examples/demo/main.zig +++ b/src/examples/demo/main.zig @@ -75,7 +75,8 @@ pub fn main() !void { /// Handler for GET / fn index(app: *App, _: *httpz.Request, res: *httpz.Response) !void { - const html = app.view.render(app.allocator, "index", .{ + // Use res.arena - memory is automatically freed after response is sent + const html = app.view.render(res.arena, "index", .{ .title = "Home", .authenticated = true, }) catch |err| { @@ -90,7 +91,7 @@ fn index(app: *App, _: *httpz.Request, res: *httpz.Response) !void { /// Handler for GET /page-a - demonstrates extends and block override fn pageA(app: *App, _: *httpz.Request, res: *httpz.Response) !void { - const html = app.view.render(app.allocator, "page-a", .{ + const html = app.view.render(res.arena, "page-a", .{ .title = "Page A - Pets", .items = &[_][]const u8{ "A", "B", "C" }, .n = 0, @@ -106,7 +107,7 @@ fn pageA(app: *App, _: *httpz.Request, res: *httpz.Response) !void { /// Handler for GET /page-b - demonstrates sub-layout inheritance fn pageB(app: *App, _: *httpz.Request, res: *httpz.Response) !void { - const html = app.view.render(app.allocator, "page-b", .{ + const html = app.view.render(res.arena, "page-b", .{ .title = "Page B - Sub Layout", }) catch |err| { res.status = 500; @@ -120,7 +121,7 @@ fn pageB(app: *App, _: *httpz.Request, res: *httpz.Response) !void { /// Handler for GET /append - demonstrates block append fn pageAppend(app: *App, _: *httpz.Request, res: *httpz.Response) !void { - const html = app.view.render(app.allocator, "page-append", .{ + const html = app.view.render(res.arena, "page-append", .{ .title = "Page Append", }) catch |err| { res.status = 500; @@ -134,7 +135,7 @@ fn pageAppend(app: *App, _: *httpz.Request, res: *httpz.Response) !void { /// Handler for GET /append-opt - demonstrates optional block keyword fn pageAppendOptional(app: *App, _: *httpz.Request, res: *httpz.Response) !void { - const html = app.view.render(app.allocator, "page-appen-optional-blk", .{ + const html = app.view.render(res.arena, "page-appen-optional-blk", .{ .title = "Page Append Optional", }) catch |err| { res.status = 500;