libs/html-threaded/lua/html_gen.lua

193 lines

1-- html_gen.lua - LuaJIT FFI bindings for HTML Threaded Writer Library
2--
3-- This module provides parallel file writing for HTML generation.
4-- Lua generates HTML strings, C handles parallel I/O via pthreads.
5--
6-- Usage:
7-- local htmlgen = require("html_gen")
8-- local ctx = htmlgen.init(8) -- 8 threads
9-- htmlgen.add_file(ctx, "/path/to/file.html", "<html>...</html>")
10-- htmlgen.add_file(ctx, "/path/to/file2.html", "<html>...</html>")
11-- htmlgen.write_all(ctx)
12-- local stats = htmlgen.get_stats(ctx)
13-- htmlgen.destroy(ctx)
14
15local ffi = require("ffi")
16
17-- {{{ local M = {}
18local M = {}
19-- }}}
20
21-- {{{ FFI definitions
22ffi.cdef[[
23 /* Opaque context handle */
24 typedef struct HtmlGenContext HtmlGenContext;
25
26 /* Error codes */
27 typedef enum {
28 HTMLGEN_SUCCESS = 0,
29 HTMLGEN_ERROR_INIT_FAILED = -1,
30 HTMLGEN_ERROR_OUT_OF_MEMORY = -2,
31 HTMLGEN_ERROR_WRITE_FAILED = -3,
32 HTMLGEN_ERROR_INVALID_CONTEXT = -4,
33 HTMLGEN_ERROR_ALREADY_RUNNING = -5,
34 } HtmlGenResult;
35
36 /* Statistics structure */
37 typedef struct {
38 uint32_t total_files;
39 uint32_t files_written;
40 uint32_t files_failed;
41 uint64_t total_bytes;
42 double elapsed_seconds;
43 } HtmlGenStats;
44
45 /* Core API */
46 HtmlGenContext* htmlgen_init(int num_threads);
47 HtmlGenResult htmlgen_add_file(HtmlGenContext* ctx,
48 const char* filepath,
49 const char* content,
50 size_t content_len);
51 HtmlGenResult htmlgen_write_all(HtmlGenContext* ctx);
52 float htmlgen_get_progress(HtmlGenContext* ctx);
53 HtmlGenResult htmlgen_get_stats(HtmlGenContext* ctx, HtmlGenStats* stats);
54 void htmlgen_clear(HtmlGenContext* ctx);
55 void htmlgen_destroy(HtmlGenContext* ctx);
56 const char* htmlgen_error_string(HtmlGenResult result);
57]]
58-- }}}
59
60-- {{{ Load shared library
61-- Library path resolution: check environment, global, then default
62local lib_path = os.getenv("HTMLGEN_LIB") or
63 _G.HTMLGEN_LIB or
64 "/mnt/mtwo/programming/ai-stuff/neocities-modernization/libs/html-threaded/build/libhtmlgen.so"
65
66local lib = nil
67local load_success, load_err = pcall(function()
68 lib = ffi.load(lib_path)
69end)
70
71if not load_success then
72 error(string.format("Failed to load libhtmlgen.so from '%s': %s\n" ..
73 "Build with: cd libs/html-threaded && make", lib_path, load_err))
74end
75-- }}}
76
77-- {{{ Error handling helper
78local function check_result(result, operation)
79 if result ~= 0 then
80 local err_str = ffi.string(lib.htmlgen_error_string(result))
81 error(string.format("%s failed: %s (code %d)", operation, err_str, tonumber(result)))
82 end
83end
84-- }}}
85
86-- {{{ M.init
87-- Initialize HTML generator context
88-- @param num_threads Number of worker threads (0 or nil = auto-detect CPU cores)
89-- @return Context handle (must be passed to destroy when done)
90function M.init(num_threads)
91 num_threads = num_threads or 0
92 local ctx = lib.htmlgen_init(num_threads)
93 if ctx == nil then
94 error("Failed to initialize HTML generator context")
95 end
96 return ctx
97end
98-- }}}
99
100-- {{{ M.add_file
101-- Add a file to the write queue
102-- @param ctx Context handle from init()
103-- @param filepath Full path to output file
104-- @param content HTML content string
105function M.add_file(ctx, filepath, content)
106 local result = lib.htmlgen_add_file(ctx, filepath, content, #content)
107 check_result(result, "add_file")
108end
109-- }}}
110
111-- {{{ M.add_files
112-- Add multiple files at once (convenience function)
113-- @param ctx Context handle from init()
114-- @param files Array of {path=string, content=string} tables
115function M.add_files(ctx, files)
116 for _, file in ipairs(files) do
117 M.add_file(ctx, file.path, file.content)
118 end
119end
120-- }}}
121
122-- {{{ M.write_all
123-- Write all queued files using thread pool
124-- Blocks until all files are written
125-- @param ctx Context handle from init()
126function M.write_all(ctx)
127 local result = lib.htmlgen_write_all(ctx)
128 check_result(result, "write_all")
129end
130-- }}}
131
132-- {{{ M.get_progress
133-- Get current progress (0.0 to 1.0)
134-- Can be called from another thread/coroutine while write_all is running
135-- @param ctx Context handle from init()
136-- @return Progress as fraction (0.0 = not started, 1.0 = complete)
137function M.get_progress(ctx)
138 return lib.htmlgen_get_progress(ctx)
139end
140-- }}}
141
142-- {{{ M.get_stats
143-- Get statistics after write_all completes
144-- @param ctx Context handle from init()
145-- @return Table with total_files, files_written, files_failed, total_bytes, elapsed_seconds
146function M.get_stats(ctx)
147 local stats = ffi.new("HtmlGenStats")
148 local result = lib.htmlgen_get_stats(ctx, stats)
149 check_result(result, "get_stats")
150
151 return {
152 total_files = tonumber(stats.total_files),
153 files_written = tonumber(stats.files_written),
154 files_failed = tonumber(stats.files_failed),
155 total_bytes = tonumber(stats.total_bytes),
156 elapsed_seconds = stats.elapsed_seconds,
157 }
158end
159-- }}}
160
161-- {{{ M.clear
162-- Clear the file queue (reuse context for another batch)
163-- @param ctx Context handle from init()
164function M.clear(ctx)
165 lib.htmlgen_clear(ctx)
166end
167-- }}}
168
169-- {{{ M.destroy
170-- Destroy context and free all resources
171-- @param ctx Context handle from init()
172function M.destroy(ctx)
173 lib.htmlgen_destroy(ctx)
174end
175-- }}}
176
177-- {{{ M.write_files
178-- Convenience function: add files and write in one call
179-- @param files Array of {path=string, content=string} tables
180-- @param num_threads Number of worker threads (0 = auto-detect)
181-- @return Statistics table
182function M.write_files(files, num_threads)
183 local ctx = M.init(num_threads or 0)
184 M.add_files(ctx, files)
185 M.write_all(ctx)
186 local stats = M.get_stats(ctx)
187 M.destroy(ctx)
188 return stats
189end
190-- }}}
191
192return M
193