diff --git a/src/cache.zig b/src/cache.zig new file mode 100644 index 0000000..6a921c9 --- /dev/null +++ b/src/cache.zig @@ -0,0 +1,57 @@ +const std = @import("std"); +const Allocator = std.heap.ArenaAllocator; + +const parser = @import("parser.zig"); + +pub const TemplateCache = struct { + arena: Allocator, + cache: std.StringHashMapUnmanaged([]parser.Node), + + pub fn init(child_allocator: std.mem.Allocator) TemplateCache { + const arena = std.heap.ArenaAllocator.init(child_allocator); + return .{ + .arena = arena, + .cache = .{}, + }; + } + + + pub fn deinit(self: *TemplateCache) void { + self.arena.deinit(); + } + + pub fn allocator(self: *TemplateCache) std.mem.Allocator { + return self.arena.allocator(); + } + + pub fn get(self: *const TemplateCache, key: []const u8) ?[]parser.Node { + return self.cache.get(key); + } + + pub fn add(self: *TemplateCache, key: []const u8, nodes: []parser.Node) !void { + // const key_copy = try self.arena.dupe(u8, key); + // errdefer self.arena.free(key_copy); + + // try self.cache.put(self.arena, key, nodes); + const gop = try self.cache.getOrPut(self.allocator(), key); + if (!gop.found_existing) { + gop.key_ptr.* = try self.allocator().dupe(u8, key); + gop.value_ptr.* = nodes; + } + gop.value_ptr.* = nodes; + } + + pub fn invalidate(self: *TemplateCache, key: []const u8) void { + if (self.cache.getEntry(key)) |entry| { + self.arena.free(entry.key_ptr.*); + for (entry.value_ptr.*) |node| node.deinit(self.arena); + self.arena.free(entry.value_ptr.*); + _ = self.cache.remove(key); + } + } + + pub fn clear(self: *TemplateCache) void { + self.deinit(); + self.cache = .{}; + } +}; diff --git a/src/renderer.zig b/src/renderer.zig index 429e59c..9550710 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -9,6 +9,7 @@ const Value = @import("context.zig").Value; const builtin_filters = @import("filters.zig").builtin_filters; const FilterError = @import("filters.zig").FilterError; const parser = @import("parser.zig"); +const TemplateCache = @import("cache.zig").TemplateCache; pub const RenderError = FilterError || error{ OutOfMemory, @@ -44,11 +45,13 @@ pub const RenderError = FilterError || error{ pub const Renderer = struct { context: *Context, allocator: Allocator, + cache: *TemplateCache, - pub fn init(context: *Context) Renderer { + pub fn init(context: *Context, cache: *TemplateCache) Renderer { return .{ .context = context, .allocator = context.allocator(), + .cache = cache, }; } @@ -72,24 +75,7 @@ pub const Renderer = struct { }; } - pub fn render(self: *const Renderer, template: []const u8, writer: anytype) RenderError!void { - const base_template = try self.readTemplateFile(template); - defer self.allocator.free(base_template); - return self.renderString(base_template, writer); - } - - pub fn renderString(self: *const Renderer, template: []const u8, writer: anytype) RenderError!void { - var arena = std.heap.ArenaAllocator.init(self.allocator); - defer arena.deinit(); - const alloc = arena.allocator(); - - var p = parser.Parser.init(template); - const nodes = try p.parse(alloc); - defer { - for (nodes) |node| node.deinit(alloc); - alloc.free(nodes); - } - + fn renderNodes(self: *const Renderer, alloc: Allocator, nodes: []parser.Node, writer: anytype) RenderError!void { const extends_node = self.checkForExtends(nodes); if (extends_node) |ext| { @@ -110,6 +96,50 @@ pub const Renderer = struct { } } + fn renderTemplate(self: *const Renderer, template: []const u8, writer: anytype, cache_key: ?[]const u8) RenderError!void { + var arena = std.heap.ArenaAllocator.init(self.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + if (cache_key) |ck| { + if (self.cache.get(ck)) |cached_nodes| { + try self.renderNodes(alloc, cached_nodes, writer); + return; + } + } + + var p = parser.Parser.init(template); + const nodes = try p.parse(alloc); + defer { + for (nodes) |node| node.deinit(alloc); + alloc.free(nodes); + } + + if (cache_key) |ck| { + var alc = self.cache.allocator(); + var cached_nodes = try alc.alloc(parser.Node, nodes.len); + errdefer alc.free(cached_nodes); + for (nodes, 0..) |node, i| { + cached_nodes[i] = try node.clone(self.allocator); + std.debug.print("clonou {any}\n", .{cached_nodes[i]}); + } + try self.cache.add(ck, nodes); + } + + return try self.renderNodes(alloc, nodes, writer); + } + + pub fn render(self: *const Renderer, template_path: []const u8, writer: anytype) RenderError!void { + const base_template = try self.readTemplateFile(template_path); + defer self.allocator.free(base_template); + + return try self.renderTemplate(base_template, writer, template_path); + } + + pub fn renderString(self: *const Renderer, template: []const u8, writer: anytype) RenderError!void { + return try self.renderTemplate(template, writer, null); + } + fn renderWithInheritance(self: *const Renderer, alloc: Allocator, base_nodes: []parser.Node, child_nodes: []parser.Node, writer: anytype) RenderError!void { for (base_nodes) |base_node| { if (base_node.type == .block) {