From c2e755d44d43abd102e804c5de4973ed819eae09 Mon Sep 17 00:00:00 2001 From: Alex Reid Date: Sat, 18 Apr 2026 09:36:58 +0100 Subject: [PATCH 1/5] initial zig 0.16 migration --- ZIG_0.16_MIGRATION.md | 116 +++++++++++++++++++++++++++++++++++++ build.zig | 132 +++++++++++++++++++++--------------------- src/async_cache.zig | 37 +++++++----- src/async_infra.zig | 7 ++- src/lua.zig | 25 +++++--- src/lua_json_gen.zig | 8 +-- src/rtd.zig | 36 +++++++----- 7 files changed, 253 insertions(+), 108 deletions(-) create mode 100644 ZIG_0.16_MIGRATION.md diff --git a/ZIG_0.16_MIGRATION.md b/ZIG_0.16_MIGRATION.md new file mode 100644 index 0000000..03d5878 --- /dev/null +++ b/ZIG_0.16_MIGRATION.md @@ -0,0 +1,116 @@ +# Zig 0.16 Migration Guide + +This document describes the changes required to migrate zigxll from Zig 0.14/0.15 to Zig 0.16. + +## build.zig Changes + +### Compile Step Methods Moved to root_module + +Methods that were on `*std.Build.Step.Compile` have moved to `compile.root_module`: + +```zig +// Old (0.14/0.15) +xll.addIncludePath(b.path("excel/include")); +xll.addLibraryPath(b.path("excel/lib")); +xll.addCSourceFiles(.{ .root = b.path("src"), .files = &.{"file.c"} }); +xll.linkLibC(); +xll.linkSystemLibrary("user32"); + +// New (0.16) +xll.root_module.addIncludePath(b.path("excel/include")); +xll.root_module.addLibraryPath(b.path("excel/lib")); +xll.root_module.addCSourceFiles(.{ .root = b.path("src"), .files = &.{"file.c"} }); +xll.root_module.link_libc = true; +xll.root_module.linkSystemLibrary("user32", .{}); +``` + +### Environment Variables + +```zig +// Old +const home = std.process.getEnvVarOwned(b.allocator, "HOME") catch return; + +// New +const home = b.graph.environ_map.get("HOME") orelse return; +``` + +### Filesystem Access + +```zig +// Old +var dir = std.fs.openDirAbsolute(path, .{}) catch return; +dir.close(); + +// New +std.Io.Dir.accessAbsolute(b.graph.io, path, .{}) catch return; +``` + +### Path Joining + +```zig +// Old +const path = std.fs.path.join(allocator, &.{ home, ".xwin" }) catch return; + +// New +const path = std.Io.Dir.path.join(allocator, &.{ home, ".xwin" }) catch return; +``` + +## Source File Changes + +### Mutex API + +`std.Thread.Mutex` has been replaced with `std.Io.Mutex`, which requires an `Io` instance for lock/unlock operations: + +```zig +// Old +var mutex: std.Thread.Mutex = .{}; +mutex.lock(); +defer mutex.unlock(); + +// New +var mutex: std.Io.Mutex = std.Io.Mutex.init; +const io = std.Options.debug_io; +mutex.lock(io) catch {}; +defer mutex.unlock(io); +``` + +### ArrayListUnmanaged Initialization + +```zig +// Old +var list = std.ArrayListUnmanaged(u8){}; + +// New +var list: std.ArrayListUnmanaged(u8) = .empty; +``` + +### ArrayList Writer + +```zig +// Old +var list = std.ArrayListUnmanaged(u8){}; +const w = list.writer(allocator); +// ... write using w ... +return list.toOwnedSlice(allocator); + +// New +var writer = std.Io.Writer.Allocating.init(allocator); +errdefer writer.deinit(); +const w = &writer.writer; +// ... write using w ... +return writer.toOwnedSlice(); +``` + +## Cross-Compilation (xwin) Workaround + +Zig 0.16 may request `MSVCRTD.lib` (debug CRT) even in release builds when cross-compiling to Windows. The xwin SDK only includes release libraries. + +**Workaround:** Create a symlink from the release library: + +```bash +ln -sf ~/.xwin/crt/lib/x86_64/msvcrt.lib ~/.xwin/crt/lib/x86_64/MSVCRTD.lib +``` + +## Reference + +- [Zig 0.16.0 Release Notes](https://ziglang.org/download/0.16.0/release-notes.html) diff --git a/build.zig b/build.zig index 8eb4130..3be9190 100644 --- a/build.zig +++ b/build.zig @@ -40,30 +40,30 @@ pub fn build(b: *std.Build) void { xll.root_module.addImport("build_options", framework_build_options.createModule()); - xll.addIncludePath(b.path("excel/include")); - xll.addLibraryPath(b.path("excel/lib")); + xll.root_module.addIncludePath(b.path("excel/include")); + xll.root_module.addLibraryPath(b.path("excel/lib")); if (enable_lua) { - addLuaPaths(b, xll); + addLuaPaths(b, xll.root_module); } // MSVC CRT stubs — safe in XLL context (Excel already initialized the CRT) if (target.result.os.tag == .windows) { - xll.addCSourceFiles(.{ .root = b.path("src"), .files = &.{"msvc_stubs.c"} }); + xll.root_module.addCSourceFiles(.{ .root = b.path("src"), .files = &.{"msvc_stubs.c"} }); } if (builtin.os.tag == .windows) { - addNativeMsvcPaths(b, xll); + addNativeMsvcPaths(b, xll.root_module); } else { - addXwinPaths(b, xll); + addXwinPaths(b, xll, xll.root_module); } - xll.linkLibC(); - xll.linkSystemLibrary("user32"); - xll.linkSystemLibrary("xlcall32"); - xll.linkSystemLibrary("frmwrk32"); - xll.linkSystemLibrary("vcruntime"); - xll.linkSystemLibrary("ucrt"); + xll.root_module.link_libc = true; + xll.root_module.linkSystemLibrary("user32", .{}); + xll.root_module.linkSystemLibrary("xlcall32", .{}); + xll.root_module.linkSystemLibrary("frmwrk32", .{}); + xll.root_module.linkSystemLibrary("vcruntime", .{}); + xll.root_module.linkSystemLibrary("ucrt", .{}); const install_xll = b.addInstallFile(xll.getEmittedBin(), "lib/output.xll"); b.getInstallStep().dependOn(&install_xll.step); @@ -80,17 +80,17 @@ pub fn build(b: *std.Build) void { }), }); tests.root_module.addImport("test_options", test_options.createModule()); - tests.addIncludePath(b.path("excel/include")); + tests.root_module.addIncludePath(b.path("excel/include")); if (enable_lua) { - addLuaPaths(b, tests); + addLuaPaths(b, tests.root_module); } - tests.linkLibC(); + tests.root_module.link_libc = true; // Only link Excel libraries on Windows if (native_target.result.os.tag == .windows) { - tests.addLibraryPath(b.path("excel/lib")); - tests.linkSystemLibrary("xlcall32"); - tests.linkSystemLibrary("frmwrk32"); + tests.root_module.addLibraryPath(b.path("excel/lib")); + tests.root_module.linkSystemLibrary("xlcall32", .{}); + tests.root_module.linkSystemLibrary("frmwrk32", .{}); } const run_tests = b.addRunArtifact(tests); @@ -240,58 +240,56 @@ pub fn buildXll( const excel_include = xll_dep.path("excel/include"); const excel_lib = xll_dep.path("excel/lib"); - xll.addIncludePath(excel_include); - xll.addLibraryPath(excel_lib); xll.root_module.addIncludePath(excel_include); + xll.root_module.addLibraryPath(excel_lib); if (options.enable_lua) { - addLuaFromDep(xll_dep, xll); + addLuaFromDep(xll_dep, xll.root_module); } // MSVC CRT stubs — safe in XLL context (Excel already initialized the CRT) if (target.result.os.tag == .windows) { - xll.addCSourceFiles(.{ .root = xll_dep.path("src"), .files = &.{"msvc_stubs.c"} }); + xll.root_module.addCSourceFiles(.{ .root = xll_dep.path("src"), .files = &.{"msvc_stubs.c"} }); } // Add Windows SDK/CRT paths to both the XLL compile step and the user module, // so that any C code the user compiles (e.g. nats.c) can find vcrt/ucrt headers and libs. if (builtin.os.tag == .windows) { - addNativeMsvcPaths(b, xll); + addNativeMsvcPaths(b, xll.root_module); } else { - addXwinPaths(b, xll); + addXwinPaths(b, xll, xll.root_module); applyXwinToModule(b, options.user_module); } - xll.linkLibC(); - xll.linkSystemLibrary("user32"); - xll.linkSystemLibrary("xlcall32"); - xll.linkSystemLibrary("frmwrk32"); + xll.root_module.link_libc = true; + xll.root_module.linkSystemLibrary("user32", .{}); + xll.root_module.linkSystemLibrary("xlcall32", .{}); + xll.root_module.linkSystemLibrary("frmwrk32", .{}); // COM/RTD support - xll.linkSystemLibrary("oleaut32"); - xll.linkSystemLibrary("advapi32"); - xll.linkSystemLibrary("ole32"); - xll.linkSystemLibrary("vcruntime"); - xll.linkSystemLibrary("ucrt"); + xll.root_module.linkSystemLibrary("oleaut32", .{}); + xll.root_module.linkSystemLibrary("advapi32", .{}); + xll.root_module.linkSystemLibrary("ole32", .{}); + xll.root_module.linkSystemLibrary("vcruntime", .{}); + xll.root_module.linkSystemLibrary("ucrt", .{}); return xll; } /// Compile Lua 5.4 from source (from xll dependency) -fn addLuaFromDep(xll_dep: *std.Build.Dependency, compile: *std.Build.Step.Compile) void { +fn addLuaFromDep(xll_dep: *std.Build.Dependency, mod: *std.Build.Module) void { const lua_src = xll_dep.path("deps/lua/src"); - compile.addIncludePath(lua_src); - compile.root_module.addIncludePath(lua_src); + mod.addIncludePath(lua_src); const xll_framework = xll_dep.module("xll"); xll_framework.addIncludePath(lua_src); - compile.addCSourceFiles(.{ .root = lua_src, .files = &lua_sources }); + mod.addCSourceFiles(.{ .root = lua_src, .files = &lua_sources }); } /// Compile Lua 5.4 from source (for framework's own build) -fn addLuaPaths(b: *std.Build, compile: *std.Build.Step.Compile) void { - compile.addIncludePath(b.path("deps/lua/src")); - compile.addCSourceFiles(.{ .root = b.path("deps/lua/src"), .files = &lua_sources }); +fn addLuaPaths(b: *std.Build, mod: *std.Build.Module) void { + mod.addIncludePath(b.path("deps/lua/src")); + mod.addCSourceFiles(.{ .root = b.path("deps/lua/src"), .files = &lua_sources }); } const lua_sources = .{ @@ -306,7 +304,7 @@ const lua_sources = .{ }; fn requireEnv(b: *std.Build, name: []const u8) []const u8 { - return std.process.getEnvVarOwned(b.allocator, name) catch { + return b.graph.environ_map.get(name) orelse { std.log.err("Missing environment variable '{s}'. Run from a Visual Studio Developer Command Prompt (vcvarsall.bat).", .{name}); @panic("MSVC environment not configured"); }; @@ -314,7 +312,7 @@ fn requireEnv(b: *std.Build, name: []const u8) []const u8 { /// On native Windows, use VCToolsInstallDir / WindowsSdkDir env vars to locate the MSVC CRT. /// These are set by the Visual Studio Developer Command Prompt (vcvarsall.bat). -fn addNativeMsvcPaths(b: *std.Build, compile: *std.Build.Step.Compile) void { +fn addNativeMsvcPaths(b: *std.Build, mod: *std.Build.Module) void { const vctools = requireEnv(b, "VCToolsInstallDir"); const ucrt_sdk = requireEnv(b, "UniversalCRTSdkDir"); const ucrt_ver = requireEnv(b, "UCRTVersion"); @@ -329,14 +327,14 @@ fn addNativeMsvcPaths(b: *std.Build, compile: *std.Build.Step.Compile) void { const um_inc_dir = b.fmt("{s}Include\\{s}\\um", .{ win_sdk, win_sdk_ver }); const shared_inc_dir = b.fmt("{s}Include\\{s}\\shared", .{ win_sdk, win_sdk_ver }); - compile.addLibraryPath(.{ .cwd_relative = msvc_lib_dir }); - compile.addLibraryPath(.{ .cwd_relative = ucrt_lib_dir }); - compile.addLibraryPath(.{ .cwd_relative = kernel32_lib_dir }); + mod.addLibraryPath(.{ .cwd_relative = msvc_lib_dir }); + mod.addLibraryPath(.{ .cwd_relative = ucrt_lib_dir }); + mod.addLibraryPath(.{ .cwd_relative = kernel32_lib_dir }); - compile.addSystemIncludePath(.{ .cwd_relative = vctools_inc }); - compile.addSystemIncludePath(.{ .cwd_relative = ucrt_inc_dir }); - compile.addSystemIncludePath(.{ .cwd_relative = um_inc_dir }); - compile.addSystemIncludePath(.{ .cwd_relative = shared_inc_dir }); + mod.addSystemIncludePath(.{ .cwd_relative = vctools_inc }); + mod.addSystemIncludePath(.{ .cwd_relative = ucrt_inc_dir }); + mod.addSystemIncludePath(.{ .cwd_relative = um_inc_dir }); + mod.addSystemIncludePath(.{ .cwd_relative = shared_inc_dir }); } /// When cross-compiling from Mac/Linux, add xwin system include paths to a module @@ -345,10 +343,10 @@ fn addNativeMsvcPaths(b: *std.Build, compile: *std.Build.Step.Compile) void { fn applyXwinToModule(b: *std.Build, mod: *std.Build.Module) void { if (builtin.os.tag == .windows) return; - const home = std.process.getEnvVarOwned(b.allocator, "HOME") catch return; - const xwin_dir = std.fs.path.join(b.allocator, &.{ home, ".xwin" }) catch return; - var dir = std.fs.openDirAbsolute(xwin_dir, .{}) catch return; - dir.close(); + const home = b.graph.environ_map.get("HOME") orelse return; + const xwin_dir = std.Io.Dir.path.join(b.allocator, &.{ home, ".xwin" }) catch return; + // Check if xwin directory exists + std.Io.Dir.accessAbsolute(b.graph.io, xwin_dir, .{}) catch return; mod.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/crt/include", .{xwin_dir}) }); mod.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/sdk/include/ucrt", .{xwin_dir}) }); @@ -358,21 +356,21 @@ fn applyXwinToModule(b: *std.Build, mod: *std.Build.Module) void { /// If ~/.xwin exists (installed via `brew install xwin && xwin --accept-license splat --output ~/.xwin`), /// add its Windows SDK and CRT paths so we can cross-compile to Windows from Mac/Linux. -fn addXwinPaths(b: *std.Build, compile: *std.Build.Step.Compile) void { +fn addXwinPaths(b: *std.Build, compile: *std.Build.Step.Compile, mod: *std.Build.Module) void { // xwin is only used for cross-compiling to Windows from Mac/Linux - const home = std.process.getEnvVarOwned(b.allocator, "HOME") catch return; - const xwin_dir = std.fs.path.join(b.allocator, &.{ home, ".xwin" }) catch return; - var dir = std.fs.openDirAbsolute(xwin_dir, .{}) catch return; - dir.close(); - - compile.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/sdk/lib/um/x86_64", .{xwin_dir}) }); - compile.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/crt/lib/x86_64", .{xwin_dir}) }); - compile.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/sdk/lib/ucrt/x86_64", .{xwin_dir}) }); - - compile.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/crt/include", .{xwin_dir}) }); - compile.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/sdk/include/ucrt", .{xwin_dir}) }); - compile.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/sdk/include/um", .{xwin_dir}) }); - compile.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/sdk/include/shared", .{xwin_dir}) }); + const home = b.graph.environ_map.get("HOME") orelse return; + const xwin_dir = std.Io.Dir.path.join(b.allocator, &.{ home, ".xwin" }) catch return; + // Check if xwin directory exists + std.Io.Dir.accessAbsolute(b.graph.io, xwin_dir, .{}) catch return; + + mod.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/sdk/lib/um/x86_64", .{xwin_dir}) }); + mod.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/crt/lib/x86_64", .{xwin_dir}) }); + mod.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/sdk/lib/ucrt/x86_64", .{xwin_dir}) }); + + mod.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/crt/include", .{xwin_dir}) }); + mod.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/sdk/include/ucrt", .{xwin_dir}) }); + mod.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/sdk/include/um", .{xwin_dir}) }); + mod.addSystemIncludePath(.{ .cwd_relative = b.fmt("{s}/sdk/include/shared", .{xwin_dir}) }); // Write a libc configuration file so Zig knows where the MSVC CRT lives const libc_conf = b.fmt( diff --git a/src/async_cache.zig b/src/async_cache.zig index 69c81a3..68d53c6 100644 --- a/src/async_cache.zig +++ b/src/async_cache.zig @@ -22,19 +22,29 @@ pub const CachedResult = struct { /// The cache owns copies of all key strings. pub const AsyncCache = struct { map: std.StringHashMap(CachedResult), - mutex: std.Thread.Mutex, + mutex: std.Io.Mutex, + + const io = std.Options.debug_io; pub fn init() AsyncCache { return .{ .map = std.StringHashMap(CachedResult).init(allocator), - .mutex = .{}, + .mutex = std.Io.Mutex.init, }; } + fn lock(self: *AsyncCache) void { + self.mutex.lock(io) catch {}; + } + + fn unlock(self: *AsyncCache) void { + self.mutex.unlock(io); + } + /// Look up a topic key. Returns null on miss. pub fn get(self: *AsyncCache, key: []const u8) ?CachedResult { - self.mutex.lock(); - defer self.mutex.unlock(); + self.lock(); + defer self.unlock(); return self.map.get(key); } @@ -42,8 +52,8 @@ pub const AsyncCache = struct { /// If the key is new, the cache dupes it and owns the copy. /// If the key already exists, just updates the value. pub fn put(self: *AsyncCache, key: []const u8, result: CachedResult) void { - self.mutex.lock(); - defer self.mutex.unlock(); + self.lock(); + defer self.unlock(); // If key already exists, just update the value if (self.map.getEntry(key)) |entry| { @@ -60,8 +70,8 @@ pub const AsyncCache = struct { /// Remove all cached results, forcing async functions to re-execute. pub fn clear(self: *AsyncCache) void { - self.mutex.lock(); - defer self.mutex.unlock(); + self.lock(); + defer self.unlock(); var it = self.map.iterator(); while (it.next()) |entry| { allocator.free(@constCast(entry.key_ptr.*)); @@ -71,19 +81,20 @@ pub const AsyncCache = struct { /// Check whether a key exists (used to avoid double-spawning). pub fn contains(self: *AsyncCache, key: []const u8) bool { - self.mutex.lock(); - defer self.mutex.unlock(); + self.lock(); + defer self.unlock(); return self.map.contains(key); } }; // Global singleton — all async functions share one cache. var global_cache: ?*AsyncCache = null; -var cache_init_mutex: std.Thread.Mutex = .{}; +var cache_init_mutex: std.Io.Mutex = std.Io.Mutex.init; pub fn getGlobalCache() *AsyncCache { - cache_init_mutex.lock(); - defer cache_init_mutex.unlock(); + const io = std.Options.debug_io; + cache_init_mutex.lock(io) catch {}; + defer cache_init_mutex.unlock(io); if (global_cache) |c| return c; const c = allocator.create(AsyncCache) catch unreachable; c.* = AsyncCache.init(); diff --git a/src/async_infra.zig b/src/async_infra.zig index b6642d3..dc4160d 100644 --- a/src/async_infra.zig +++ b/src/async_infra.zig @@ -22,13 +22,14 @@ const allocator = std.heap.c_allocator; // ============================================================================ var global_pool: ?*std.Thread.Pool = null; -var pool_mutex: std.Thread.Mutex = .{}; +var pool_mutex: std.Io.Mutex = std.Io.Mutex.init; const default_pool_size = 4; +const io = std.Options.debug_io; pub fn getPool() *std.Thread.Pool { - pool_mutex.lock(); - defer pool_mutex.unlock(); + pool_mutex.lock(io) catch {}; + defer pool_mutex.unlock(io); if (global_pool) |p| return p; const p = allocator.create(std.Thread.Pool) catch unreachable; p.init(.{ diff --git a/src/lua.zig b/src/lua.zig index a026e56..3d21e0c 100644 --- a/src/lua.zig +++ b/src/lua.zig @@ -84,7 +84,8 @@ var state_pool: [pool_size]StateSlot = [_]StateSlot{.{}} ** pool_size; var pool_initialized: bool = false; /// Mutex for the main (sync) state — slot 0. -var main_state_mutex: std.Thread.Mutex = .{}; +var main_state_mutex: std.Io.Mutex = std.Io.Mutex.init; +const io = std.Options.debug_io; /// Cached script sources for loading into new states. const ScriptEntry = struct { @@ -145,17 +146,25 @@ const SharedValue = union(enum) { }; var shared_store: std.StringHashMapUnmanaged(SharedValue) = .empty; -var shared_store_mutex: std.Thread.Mutex = .{}; +var shared_store_mutex: std.Io.Mutex = std.Io.Mutex.init; + +fn lockSharedStore() void { + shared_store_mutex.lock(io) catch {}; +} + +fn unlockSharedStore() void { + shared_store_mutex.unlock(io); +} fn sharedGet(key: []const u8) ?SharedValue { - shared_store_mutex.lock(); - defer shared_store_mutex.unlock(); + lockSharedStore(); + defer unlockSharedStore(); return shared_store.get(key); } fn sharedSet(key: []const u8, value: ?SharedValue) void { - shared_store_mutex.lock(); - defer shared_store_mutex.unlock(); + lockSharedStore(); + defer unlockSharedStore(); const alloc = std.heap.c_allocator; @@ -298,12 +307,12 @@ pub fn getState() ?*lua_State { /// Lock the main state for sync use. pub fn lockMain() void { - main_state_mutex.lock(); + main_state_mutex.lock(io) catch {}; } /// Unlock the main state after sync use. pub fn unlockMain() void { - main_state_mutex.unlock(); + main_state_mutex.unlock(io); } /// Acquire any free state from the pool for async use. diff --git a/src/lua_json_gen.zig b/src/lua_json_gen.zig index 4f53ef3..4cf4f2b 100644 --- a/src/lua_json_gen.zig +++ b/src/lua_json_gen.zig @@ -16,9 +16,9 @@ pub const LuaJsonError = error{ /// Generate Zig source from a JSON function definitions file. /// Returns the generated source as a string owned by `allocator`. pub fn generate(allocator: std.mem.Allocator, json_bytes: []const u8) ![]const u8 { - var list = std.ArrayListUnmanaged(u8){}; - errdefer list.deinit(allocator); - const w = list.writer(allocator); + var writer = std.Io.Writer.Allocating.init(allocator); + errdefer writer.deinit(); + const w = &writer.writer; try w.writeAll( \\// Auto-generated from lua_functions.json — do not edit @@ -110,7 +110,7 @@ pub fn generate(allocator: std.mem.Allocator, json_bytes: []const u8) ![]const u try w.writeAll("});\n\n"); } - return list.toOwnedSlice(allocator); + return writer.toOwnedSlice(); } fn getStr(obj: std.json.ObjectMap, key: []const u8) ?[]const u8 { diff --git a/src/rtd.zig b/src/rtd.zig index 1f02cc1..9ae2511 100644 --- a/src/rtd.zig +++ b/src/rtd.zig @@ -293,11 +293,21 @@ pub const RtdContext = struct { /// through a reallocated hashmap backing array. All built-in access /// to `topics` inside this file holds this mutex; handlers should /// also lock it whenever they touch `ctx.topics` from a worker. - topics_mu: std.Thread.Mutex = .{}, + topics_mu: std.Io.Mutex = std.Io.Mutex.init, pending_connects: PendingConnectList = .empty, topic_count: usize = 0, user_data: ?*anyopaque = null, + const io = std.Options.debug_io; + + pub fn lockTopics(self: *RtdContext) void { + self.topics_mu.lock(io) catch {}; + } + + pub fn unlockTopics(self: *RtdContext) void { + self.topics_mu.unlock(io); + } + /// Call UpdateNotify on Excel's callback to trigger RefreshData. pub fn notifyExcel(self: *RtdContext) void { if (self.update_event) |evt| { @@ -307,8 +317,8 @@ pub const RtdContext = struct { /// Mark all active topics as dirty. pub fn markAllDirty(self: *RtdContext) void { - self.topics_mu.lock(); - defer self.topics_mu.unlock(); + self.lockTopics(); + defer self.unlockTopics(); var it = self.topics.iterator(); while (it.next()) |entry| { entry.value_ptr.dirty = true; @@ -318,8 +328,8 @@ pub const RtdContext = struct { pub fn deinit(self: *RtdContext) void { // deinit runs during onTerminate; by contract all workers are gone // by this point so no lock is needed. Taking it anyway for symmetry. - self.topics_mu.lock(); - defer self.topics_mu.unlock(); + self.lockTopics(); + defer self.unlockTopics(); var it = self.topics.iterator(); while (it.next()) |entry| { freeTopicStrings(entry.value_ptr.strings); @@ -560,8 +570,8 @@ pub fn RtdServer(comptime Handler: type, comptime config: RtdConfig) type { const s = getObj(self_opaque).getState(); { - s.ctx.topics_mu.lock(); - defer s.ctx.topics_mu.unlock(); + s.ctx.lockTopics(); + defer s.ctx.unlockTopics(); s.ctx.topics.put(topic_id, .{ .dirty = true, .strings = topic_strings }) catch { freeTopicStrings(topic_strings); return E_FAIL; @@ -646,8 +656,8 @@ pub fn RtdServer(comptime Handler: type, comptime config: RtdConfig) type { var dirty_count: ULONG = 0; { - s.ctx.topics_mu.lock(); - defer s.ctx.topics_mu.unlock(); + s.ctx.lockTopics(); + defer s.ctx.unlockTopics(); var it = s.ctx.topics.iterator(); while (it.next()) |entry| { if (entry.value_ptr.dirty) dirty_count += 1; @@ -677,8 +687,8 @@ pub fn RtdServer(comptime Handler: type, comptime config: RtdConfig) type { var dirty_ids: [256]LONG = undefined; var dirty_n: usize = 0; { - s.ctx.topics_mu.lock(); - defer s.ctx.topics_mu.unlock(); + s.ctx.lockTopics(); + defer s.ctx.unlockTopics(); var it = s.ctx.topics.iterator(); while (it.next()) |entry| { if (entry.value_ptr.dirty) { @@ -722,8 +732,8 @@ pub fn RtdServer(comptime Handler: type, comptime config: RtdConfig) type { const s = getObj(self_opaque).getState(); { - s.ctx.topics_mu.lock(); - defer s.ctx.topics_mu.unlock(); + s.ctx.lockTopics(); + defer s.ctx.unlockTopics(); if (s.ctx.topics.fetchRemove(topic_id)) |entry| { freeTopicStrings(entry.value.strings); } From 4fe913662fcb9e0e3f91793b87779676e4c34f99 Mon Sep 17 00:00:00 2001 From: Alex Reid Date: Sat, 18 Apr 2026 09:46:18 +0100 Subject: [PATCH 2/5] build --- .github/workflows/build.yml | 2 +- README.md | 6 ++++++ build.zig.zon | 2 +- example/build.zig.zon | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 603d35b..1ff88e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,7 +45,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: 0.15.1 + version: 0.16.0 - name: Run tests run: zig build test --summary all diff --git a/README.md b/README.md index 2b32f91..a418d24 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,12 @@ xwin --accept-license splat --output ~/.xwin If you don't have Cargo, [install Rust](https://rustup.rs/) or grab a prebuilt binary from the [releases page](https://github.com/Jake-Shadle/xwin/releases). +**Debug CRT:** xwin only includes release CRT libraries. If you get linker errors about `MSVCRTD.lib`, create a symlink: + +```bash +ln -s ~/.xwin/crt/lib/x86_64/msvcrt.lib ~/.xwin/crt/lib/x86_64/MSVCRTD.lib +``` + Once set up, `zig build` auto-detects `~/.xwin` and cross-compiles. ## Dependencies diff --git a/build.zig.zon b/build.zig.zon index cbb6f0b..1c8d62d 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,7 +2,7 @@ .name = .xll, .version = "0.3.27", .fingerprint = 0x28efe92271313caf, // Changing this has security and trust implications. - .minimum_zig_version = "0.15.1", + .minimum_zig_version = "0.16.0", .paths = .{ "build.zig", "build.zig.zon", diff --git a/example/build.zig.zon b/example/build.zig.zon index 093b952..20585c1 100644 --- a/example/build.zig.zon +++ b/example/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .my_excel_functions, .version = "0.1.0", - .minimum_zig_version = "0.15.1", + .minimum_zig_version = "0.16.0", .fingerprint = 0x2cf9360d66192df2, .dependencies = .{ From 81065cc4329b2a4575e3d8212ed64354f62768b3 Mon Sep 17 00:00:00 2001 From: Alex Reid Date: Sat, 18 Apr 2026 09:52:21 +0100 Subject: [PATCH 3/5] 0.4 --- build.zig.zon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index 1c8d62d..b4f436b 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .xll, - .version = "0.3.27", + .version = "0.4.0", .fingerprint = 0x28efe92271313caf, // Changing this has security and trust implications. .minimum_zig_version = "0.16.0", .paths = .{ From 95059df66d8ea42c9bc97e921baf176b28452c87 Mon Sep 17 00:00:00 2001 From: Alex Reid Date: Sat, 18 Apr 2026 09:59:01 +0100 Subject: [PATCH 4/5] missed io param --- ZIG_0.16_MIGRATION.md | 10 ++++++++++ build.zig | 1 + 2 files changed, 11 insertions(+) diff --git a/ZIG_0.16_MIGRATION.md b/ZIG_0.16_MIGRATION.md index 03d5878..34abb78 100644 --- a/ZIG_0.16_MIGRATION.md +++ b/ZIG_0.16_MIGRATION.md @@ -55,6 +55,16 @@ const path = std.fs.path.join(allocator, &.{ home, ".xwin" }) catch return; const path = std.Io.Dir.path.join(allocator, &.{ home, ".xwin" }) catch return; ``` +### File Reading + +```zig +// Old +const bytes = dir.handle.readFileAlloc(allocator, sub_path, max_size) catch ...; + +// New (requires io parameter) +const bytes = dir.handle.readFileAlloc(b.graph.io, allocator, sub_path, max_size) catch ...; +``` + ## Source File Changes ### Mutex API diff --git a/build.zig b/build.zig index 3be9190..1de6318 100644 --- a/build.zig +++ b/build.zig @@ -171,6 +171,7 @@ pub fn buildXll( const lua_json_gen = @import("src/lua_json_gen.zig"); const path3 = json_path.getPath3(b, null); const json_bytes = path3.root_dir.handle.readFileAlloc( + b.graph.io, b.allocator, path3.sub_path, 1024 * 1024, From fa9e6b8f4f9077edda75fc5580847601f8f812ee Mon Sep 17 00:00:00 2001 From: Alex Reid Date: Sat, 18 Apr 2026 10:16:07 +0100 Subject: [PATCH 5/5] fix example --- ZIG_0.16_MIGRATION.md | 90 +++++++++++++++++++++++++++++++++------ build.zig | 14 +++--- example/src/timer_rtd.zig | 2 +- src/async_infra.zig | 37 +++++++--------- src/lua_builtins.zig | 64 ++++++++++++++-------------- src/lua_function.zig | 13 +++--- src/rtd.zig | 2 +- 7 files changed, 141 insertions(+), 81 deletions(-) diff --git a/ZIG_0.16_MIGRATION.md b/ZIG_0.16_MIGRATION.md index 34abb78..71df0e7 100644 --- a/ZIG_0.16_MIGRATION.md +++ b/ZIG_0.16_MIGRATION.md @@ -61,8 +61,34 @@ const path = std.Io.Dir.path.join(allocator, &.{ home, ".xwin" }) catch return; // Old const bytes = dir.handle.readFileAlloc(allocator, sub_path, max_size) catch ...; -// New (requires io parameter) -const bytes = dir.handle.readFileAlloc(b.graph.io, allocator, sub_path, max_size) catch ...; +// New (requires io parameter, sub_path before allocator, Io.Limit for max_size) +const bytes = dir.handle.readFileAlloc(b.graph.io, sub_path, allocator, std.Io.Limit.limited(max_size)) catch ...; +``` + +### Directory Operations + +```zig +// Old +var dir = std.fs.cwd().openDir(path, .{ .iterate = true }) catch ...; +defer dir.close(); +var it = dir.iterate(); +while (it.next() catch ...) |entry| { ... } + +// New (cwd() takes no args, openDir/close/next require io parameter) +var dir = std.Io.Dir.cwd().openDir(b.graph.io, path, .{ .iterate = true }) catch ...; +defer dir.close(b.graph.io); +var it = dir.iterate(); +while (it.next(b.graph.io) catch ...) |entry| { ... } +``` + +### captureStdOut + +```zig +// Old +const output = run.captureStdOut(); + +// New (requires options struct) +const output = run.captureStdOut(.{}); ``` ## Source File Changes @@ -94,21 +120,61 @@ var list = std.ArrayListUnmanaged(u8){}; var list: std.ArrayListUnmanaged(u8) = .empty; ``` -### ArrayList Writer +### ArrayList API Changes + +The `writer()` method has been removed from ArrayList. Use ArrayList methods directly: ```zig // Old -var list = std.ArrayListUnmanaged(u8){}; -const w = list.writer(allocator); -// ... write using w ... -return list.toOwnedSlice(allocator); +var buf = std.ArrayListUnmanaged(u8){}; +errdefer buf.deinit(allocator); +const writer = buf.writer(allocator); +try writer.writeAll("hello"); +try writer.writeByte('|'); +try writer.print("{d}", .{42}); +return buf.toOwnedSlice(allocator); + +// New (use ArrayList methods directly, allocator passed to each method) +var buf: std.ArrayList(u8) = .empty; +errdefer buf.deinit(allocator); +try buf.appendSlice(allocator, "hello"); +try buf.append(allocator, '|'); +try buf.print(allocator, "{d}", .{42}); +return buf.toOwnedSlice(allocator); +``` + +Note: `std.ArrayList` in 0.16 is now the unmanaged version (formerly `ArrayListUnmanaged`). +The managed version is `std.array_list.Managed` but is deprecated. + +### Thread.sleep Removed + +`std.Thread.sleep` has been removed. For Windows targets, use the Win32 API directly +(this is what `std.Thread.sleep` called internally anyway): + +```zig +// Old +std.Thread.sleep(2 * std.time.ns_per_s); + +// New (Windows) - use kernel32.Sleep (milliseconds) +const kernel32 = struct { + extern "kernel32" fn Sleep(dwMilliseconds: u32) callconv(.winapi) void; +}; +kernel32.Sleep(2000); // 2 seconds + +// Or use the zigxll helper: +xll.async_infra.sleepMs(2000); +``` + +### Windows Types Removed + +`std.os.windows.HRESULT` has been removed. Define locally: + +```zig +// Old +pub const HRESULT = std.os.windows.HRESULT; // New -var writer = std.Io.Writer.Allocating.init(allocator); -errdefer writer.deinit(); -const w = &writer.writer; -// ... write using w ... -return writer.toOwnedSlice(); +pub const HRESULT = i32; ``` ## Cross-Compilation (xwin) Workaround diff --git a/build.zig b/build.zig index 1de6318..16377a9 100644 --- a/build.zig +++ b/build.zig @@ -172,9 +172,9 @@ pub fn buildXll( const path3 = json_path.getPath3(b, null); const json_bytes = path3.root_dir.handle.readFileAlloc( b.graph.io, - b.allocator, path3.sub_path, - 1024 * 1024, + b.allocator, + std.Io.Limit.limited(1024 * 1024), ) catch @panic("Failed to read lua_json file"); const generated_src = lua_json_gen.generate(b.allocator, json_bytes) catch @panic("Failed to generate Lua function definitions from JSON"); @@ -195,13 +195,13 @@ pub fn buildXll( var all_scripts: std.ArrayListUnmanaged([]const u8) = .empty; for (options.lua_scripts) |s| all_scripts.append(b.allocator, s) catch @panic("OOM"); if (options.lua_scripts_dir) |dir_path| { - var dir = std.fs.cwd().openDir(dir_path, .{ .iterate = true }) catch + var dir = std.Io.Dir.cwd().openDir(b.graph.io, dir_path, .{ .iterate = true }) catch @panic("cannot open lua_scripts_dir"); - defer dir.close(); + defer dir.close(b.graph.io); var it = dir.iterate(); - while (it.next() catch @panic("lua_scripts_dir iterate failed")) |entry| { + while (it.next(b.graph.io) catch @panic("lua_scripts_dir iterate failed")) |entry| { if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".lua")) { - const full = std.fs.path.join(b.allocator, &.{ dir_path, entry.name }) catch @panic("OOM"); + const full = std.Io.Dir.path.join(b.allocator, &.{ dir_path, entry.name }) catch @panic("OOM"); all_scripts.append(b.allocator, full) catch @panic("OOM"); } } @@ -215,7 +215,7 @@ pub fn buildXll( lua_gen.setCwd(b.path(".")); lua_gen.addArgs(&.{ "--prefix", lua_prefix, "--category", lua_category, "--embed-root", "src" }); for (all_scripts.items) |script| lua_gen.addArg(script); - const lua_generated = lua_gen.captureStdOut(); + const lua_generated = lua_gen.captureStdOut(.{}); // Write generated file to user's source tree (for IDE support and @embedFile resolution) const update_src = b.addUpdateSourceFiles(); diff --git a/example/src/timer_rtd.zig b/example/src/timer_rtd.zig index fa7a903..225b642 100644 --- a/example/src/timer_rtd.zig +++ b/example/src/timer_rtd.zig @@ -37,7 +37,7 @@ const TimerHandler = struct { fn timerProc(self: *TimerHandler, ctx: *rtd.RtdContext) void { rtd.debugLog("Timer thread started", .{}); while (self.running.load(.acquire)) { - std.Thread.sleep(2 * std.time.ns_per_s); + xll.async_infra.sleepMs(2000); if (!self.running.load(.acquire)) break; self.counter += 1; diff --git a/src/async_infra.zig b/src/async_infra.zig index dc4160d..9029852 100644 --- a/src/async_infra.zig +++ b/src/async_infra.zig @@ -45,29 +45,29 @@ pub fn getPool() *std.Thread.Pool { // ============================================================================ /// Append a serialized argument value to the key buffer. -fn appendArg(writer: anytype, comptime T: type, arg: T) !void { +fn appendArg(buf: *std.ArrayList(u8), comptime T: type, arg: T) !void { // Handle optional types const type_info = @typeInfo(T); if (type_info == .optional) { if (arg) |val| { - try appendArg(writer, type_info.optional.child, val); + try appendArg(buf, type_info.optional.child, val); } else { - try writer.writeAll(""); + try buf.appendSlice(allocator, ""); } return; } if (T == f64) { - try writer.print("{d:.15}", .{arg}); + try buf.print(allocator, "{d:.15}", .{arg}); } else if (T == bool) { - try writer.writeAll(if (arg) "T" else "F"); + try buf.appendSlice(allocator, if (arg) "T" else "F"); } else if (T == []const u8) { - try writer.writeAll(arg); + try buf.appendSlice(allocator, arg); } else if (T == [][]const f64) { - try writer.print("[{d}x{d}]", .{ arg.len, if (arg.len > 0) arg[0].len else 0 }); + try buf.print(allocator, "[{d}x{d}]", .{ arg.len, if (arg.len > 0) arg[0].len else 0 }); for (arg) |row| { for (row) |v| { - try writer.print(",{d:.10}", .{v}); + try buf.print(allocator, ",{d:.10}", .{v}); } } } @@ -76,14 +76,13 @@ fn appendArg(writer: anytype, comptime T: type, arg: T) !void { /// Build a topic key from function name and arguments. /// Returns a heap-allocated string owned by the caller. pub fn buildTopicKey(comptime name: []const u8, comptime ParamTypes: []const type, args: anytype) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .empty; + var buf: std.ArrayList(u8) = .empty; errdefer buf.deinit(allocator); - const writer = buf.writer(allocator); - try writer.writeAll(name); + try buf.appendSlice(allocator, name); inline for (0..ParamTypes.len) |i| { - try writer.writeByte('|'); - try appendArg(writer, ParamTypes[i], args[i]); + try buf.append(allocator, '|'); + try appendArg(&buf, ParamTypes[i], args[i]); } return buf.toOwnedSlice(allocator); @@ -202,14 +201,10 @@ extern "ole32" fn CoInitializeEx(reserved: ?*anyopaque, co_init: u32) callconv(. /// Windows-safe sleep. std.Thread.sleep may not work on cross-compiled targets. pub fn sleepMs(ms: u32) void { - if (@import("builtin").os.tag == .windows) { - const kernel32 = struct { - extern "kernel32" fn Sleep(dwMilliseconds: u32) callconv(.winapi) void; - }; - kernel32.Sleep(ms); - } else { - std.Thread.sleep(@as(u64, ms) * std.time.ns_per_ms); - } + const kernel32 = struct { + extern "kernel32" fn Sleep(dwMilliseconds: u32) callconv(.winapi) void; + }; + kernel32.Sleep(ms); } /// Spawn a worker thread using Windows CreateThread directly. diff --git a/src/lua_builtins.zig b/src/lua_builtins.zig index 7fb15e6..bcbb0ec 100644 --- a/src/lua_builtins.zig +++ b/src/lua_builtins.zig @@ -76,10 +76,10 @@ fn pushJsonValue(L: *c.lua_State, value: std.json.Value) void { /// xllify.json_stringify(value) -> string fn luaJsonStringify(L: ?*c.lua_State) callconv(.c) c_int { const state = L orelse return 0; - var buf: std.ArrayList(u8) = .{}; + var buf: std.ArrayList(u8) = .empty; defer buf.deinit(allocator); - luaValueToJson(state, 1, buf.writer(allocator), 0) catch { + luaValueToJson(state, 1, &buf, 0) catch { _ = c.lua_pushlstring(state, "null", 4); return 1; }; @@ -89,40 +89,40 @@ fn luaJsonStringify(L: ?*c.lua_State) callconv(.c) c_int { } /// Serialise the Lua value at `idx` to JSON. -fn luaValueToJson(L: *c.lua_State, idx: c_int, writer: anytype, depth: u32) !void { +fn luaValueToJson(L: *c.lua_State, idx: c_int, buf: *std.ArrayList(u8), depth: u32) !void { if (depth > 50) return error.TooDeep; const abs_idx = if (idx < 0) c.lua_gettop(L) + idx + 1 else idx; const t = c.lua_type(L, abs_idx); switch (t) { - c.LUA_TNIL => try writer.writeAll("null"), + c.LUA_TNIL => try buf.appendSlice(allocator, "null"), c.LUA_TBOOLEAN => { if (c.lua_toboolean(L, abs_idx) != 0) - try writer.writeAll("true") + try buf.appendSlice(allocator, "true") else - try writer.writeAll("false"); + try buf.appendSlice(allocator, "false"); }, c.LUA_TNUMBER => { if (c.lua_isinteger(L, abs_idx) != 0) { const n = c.lua_tointegerx(L, abs_idx, null); - try std.fmt.format(writer, "{d}", .{n}); + try buf.print(allocator, "{d}", .{n}); } else { const n = c.lua_tonumberx(L, abs_idx, null); if (std.math.isNan(n) or std.math.isInf(n)) { - try writer.writeAll("null"); + try buf.appendSlice(allocator, "null"); } else { - try std.fmt.format(writer, "{d}", .{n}); + try buf.print(allocator, "{d}", .{n}); } } }, c.LUA_TSTRING => { var slen: usize = 0; const sptr = c.lua_tolstring(L, abs_idx, &slen) orelse { - try writer.writeAll("null"); + try buf.appendSlice(allocator, "null"); return; }; - try writeJsonString(writer, sptr[0..slen]); + try writeJsonString(buf, sptr[0..slen]); }, c.LUA_TTABLE => { // Detect array vs object: if key 1 exists, treat as array @@ -131,62 +131,62 @@ fn luaValueToJson(L: *c.lua_State, idx: c_int, writer: anytype, depth: u32) !voi c.lua_settop(L, c.lua_gettop(L) - 1); if (is_array) { - try writer.writeByte('['); + try buf.append(allocator, '['); const len = c.luaL_len(L, abs_idx); var i: c.lua_Integer = 1; while (i <= len) : (i += 1) { - if (i > 1) try writer.writeByte(','); + if (i > 1) try buf.append(allocator, ','); _ = c.lua_rawgeti(L, abs_idx, i); - try luaValueToJson(L, -1, writer, depth + 1); + try luaValueToJson(L, -1, buf, depth + 1); c.lua_settop(L, c.lua_gettop(L) - 1); } - try writer.writeByte(']'); + try buf.append(allocator, ']'); } else { - try writer.writeByte('{'); + try buf.append(allocator, '{'); var first = true; c.lua_pushnil(L); while (c.lua_next(L, abs_idx) != 0) { // Only string keys in JSON objects if (c.lua_type(L, -2) == c.LUA_TSTRING) { - if (!first) try writer.writeByte(','); + if (!first) try buf.append(allocator, ','); first = false; var klen: usize = 0; const kptr = c.lua_tolstring(L, -2, &klen) orelse { c.lua_settop(L, c.lua_gettop(L) - 1); continue; }; - try writeJsonString(writer, kptr[0..klen]); - try writer.writeByte(':'); - try luaValueToJson(L, -1, writer, depth + 1); + try writeJsonString(buf, kptr[0..klen]); + try buf.append(allocator, ':'); + try luaValueToJson(L, -1, buf, depth + 1); } c.lua_settop(L, c.lua_gettop(L) - 1); } - try writer.writeByte('}'); + try buf.append(allocator, '}'); } }, - else => try writer.writeAll("null"), + else => try buf.appendSlice(allocator, "null"), } } -fn writeJsonString(writer: anytype, s: []const u8) !void { - try writer.writeByte('"'); +fn writeJsonString(buf: *std.ArrayList(u8), s: []const u8) !void { + try buf.append(allocator, '"'); for (s) |ch| { switch (ch) { - '"' => try writer.writeAll("\\\""), - '\\' => try writer.writeAll("\\\\"), - '\n' => try writer.writeAll("\\n"), - '\r' => try writer.writeAll("\\r"), - '\t' => try writer.writeAll("\\t"), + '"' => try buf.appendSlice(allocator, "\\\""), + '\\' => try buf.appendSlice(allocator, "\\\\"), + '\n' => try buf.appendSlice(allocator, "\\n"), + '\r' => try buf.appendSlice(allocator, "\\r"), + '\t' => try buf.appendSlice(allocator, "\\t"), else => { if (ch < 0x20) { - try std.fmt.format(writer, "\\u{x:0>4}", .{ch}); + try buf.print(allocator, "\\u{x:0>4}", .{ch}); } else { - try writer.writeByte(ch); + try buf.append(allocator, ch); } }, } } - try writer.writeByte('"'); + try buf.append(allocator, '"'); } // ============================================================================ diff --git a/src/lua_function.zig b/src/lua_function.zig index da95f28..ef124a2 100644 --- a/src/lua_function.zig +++ b/src/lua_function.zig @@ -252,27 +252,26 @@ pub fn LuaFunction(comptime meta: anytype) type { /// Build a topic key from function name + serialized Lua args. fn buildLuaTopicKey(args: [param_count]*xl.XLOPER12) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .empty; + var buf: std.ArrayList(u8) = .empty; errdefer buf.deinit(allocator); - const writer = buf.writer(allocator); - try writer.writeAll(name); + try buf.appendSlice(allocator, name); inline for (0..param_count) |i| { - try writer.writeByte('|'); + try buf.append(allocator, '|'); const val = XLValue.fromXLOPER12(allocator, args[i].*, false); switch (lua_params[i].type) { .number => { const num = val.as_double() catch 0; - try writer.print("{d:.15}", .{num}); + try buf.print(allocator, "{d:.15}", .{num}); }, .string => { const str = val.as_utf8str() catch ""; defer if (str.len > 0) allocator.free(str); - try writer.writeAll(str); + try buf.appendSlice(allocator, str); }, .boolean => { const b = val.as_bool() catch false; - try writer.writeAll(if (b) "T" else "F"); + try buf.appendSlice(allocator, if (b) "T" else "F"); }, } } diff --git a/src/rtd.zig b/src/rtd.zig index 9ae2511..a983556 100644 --- a/src/rtd.zig +++ b/src/rtd.zig @@ -29,7 +29,7 @@ const windows = std.os.windows; // Magic values ahoy // ============================================================================ -pub const HRESULT = windows.HRESULT; +pub const HRESULT = i32; // Defined locally as Zig 0.16 removed std.os.windows.HRESULT pub const ULONG = windows.ULONG; pub const LONG = c_long; pub const GUID = windows.GUID;