diff --git a/.gitignore b/.gitignore index af614f13b..71396f911 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ **/*.DS_Store **/*.zig-cache +# Ignore build directories +*/build/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..14458c274 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,10 @@ +# Contributing + +Please write clear commit messages that briefly describe the changes. +For example: + +``` +Add Cell framework example using C++23 modules +``` + +Commit messages like "Applying previous commit" should be avoided. diff --git a/README.md b/README.md index 9fc04abd2..c33b2a3ac 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ### Quick Start -A simple command-line client is provided in `agent_client.zig`. Set the `OPENAI_API_KEY` environment variable and run: +A simple command-line client is provided in `agent_client.zig`. Make sure Zig 0.14.1 is installed (see ). Set the `OPENAI_API_KEY` environment variable and run: ```bash zig run agent_client.zig -- --persona Abbey @@ -30,3 +30,6 @@ To predict a probability with the trained model: zig run local_ml.zig -- predict model.txt 1.2 3.4 ``` + +### Cell Framework Example +This repository now includes a demonstration of the Cell framework using modern C++23 modules. See `cell_framework/README.md` for build instructions. diff --git a/agent_client.zig b/agent_client.zig new file mode 100644 index 000000000..51770f506 --- /dev/null +++ b/agent_client.zig @@ -0,0 +1,5 @@ +const Agent = @import("./src/agent.zig"); + +pub fn main() !void { + try Agent.main(); +} diff --git a/build.zig b/build.zig index b6884115e..fc46c70d5 100644 --- a/build.zig +++ b/build.zig @@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - // ─── Feature flags for conditional compilation ─────────────────────────── + // Feature flags for conditional compilation const options = b.addOptions(); options.addOption(bool, "enable_gpu", b.option(bool, "gpu", "Enable GPU acceleration") orelse detectGPUSupport()); options.addOption(bool, "enable_simd", b.option(bool, "simd", "Enable SIMD optimizations") orelse detectSIMDSupport()); @@ -20,26 +20,26 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "abi", - .root_source_file = b.path("src/main.zig"), + .root_source_file = .{ .src_path = .{ .owner = b, .sub_path = "src/main.zig" } }, .target = target, .optimize = platform_optimize, }); - // ─── Optimization flags ────────────────────────────────────────────────── + // Optimization flags exe.link_function_sections = true; exe.link_gc_sections = true; if (platform_optimize == .ReleaseSmall or platform_optimize == .ReleaseFast) { exe.root_module.strip = true; } - // No external dependencies currently required. + // Dependencies exe.root_module.addOptions("build_options", options); - // ─── Platform-specific dependencies ────────────────────────────────────── + // Platform-specific dependencies switch (target.result.os.tag) { .linux => { exe.linkSystemLibrary("c"); - if (b.option(bool, "enable_io_uring", "Enable io_uring support") orelse true) { + if (b.option(bool, "enable_io_uring", "Enable io_uring support") orelse false) { exe.linkSystemLibrary("uring"); } }, @@ -68,7 +68,7 @@ pub fn build(b: *std.Build) void { const unit_tests = b.addTest(.{ .root_source_file = b.path("src/main.zig"), .target = target, - .optimize = optimize, + .optimize = platform_optimize, }); unit_tests.root_module.addOptions("build_options", options); test_step.dependOn(&b.addRunArtifact(unit_tests).step); diff --git a/cell_framework/CMakeLists.txt b/cell_framework/CMakeLists.txt new file mode 100644 index 000000000..7253c3852 --- /dev/null +++ b/cell_framework/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.26) +project(CellFramework LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API 1) + +# Directory for generated headers +set(GENERATED_INCLUDE_DIR ${CMAKE_BINARY_DIR}/include) +file(MAKE_DIRECTORY ${GENERATED_INCLUDE_DIR}) + +add_custom_target(generate_headers + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/generate_headers.cmake + BYPRODUCTS ${GENERATED_INCLUDE_DIR}/Cell/Core.hpp + VERBATIM +) + +add_library(CellCore) + +target_sources(CellCore + PUBLIC + FILE_SET cxx_modules TYPE CXX_MODULES FILES + ${CMAKE_CURRENT_SOURCE_DIR}/Cell/Core.ixx + FILE_SET cxx_modules TYPE CXX_MODULES FILES + ${CMAKE_CURRENT_SOURCE_DIR}/Cell/Core.cpp +) + +target_include_directories(CellCore PUBLIC ${GENERATED_INCLUDE_DIR}) + +add_dependencies(CellCore generate_headers) + +add_executable(cell_app main.cpp) + +target_link_libraries(cell_app PRIVATE CellCore) + +add_dependencies(cell_app generate_headers) diff --git a/cell_framework/Cell/Core.cpp b/cell_framework/Cell/Core.cpp new file mode 100644 index 000000000..a0ed2f418 --- /dev/null +++ b/cell_framework/Cell/Core.cpp @@ -0,0 +1,11 @@ +module; +#include + +module Cell.Core; + +namespace Cell { + void Engine::run() { + std::cout << "Cell Engine running!" << std::endl; + std::cout << "2 + 2 = " << add(2, 2) << std::endl; + } +} diff --git a/cell_framework/Cell/Core.ixx b/cell_framework/Cell/Core.ixx new file mode 100644 index 000000000..609ae65a7 --- /dev/null +++ b/cell_framework/Cell/Core.ixx @@ -0,0 +1,14 @@ +export module Cell.Core; + +export import ; + +export namespace Cell { + inline int add(int a, int b) { + return a + b; + } + + class Engine { + public: + void run(); + }; +} diff --git a/cell_framework/README.md b/cell_framework/README.md new file mode 100644 index 000000000..f4518f423 --- /dev/null +++ b/cell_framework/README.md @@ -0,0 +1,27 @@ +# Cell Framework Example with C++23 Modules + +This example demonstrates the Cell framework using a modules-first design. +The build system uses CMake 3.26+ with the experimental C++ module API and +automatically generates traditional headers from module interface files. + +## Prerequisites + +- CMake 3.26 or newer +- A C++23 compiler with module support (Clang 18+ or equivalent) + +## Building + +```bash +mkdir build && cd build +cmake .. +cmake --build . +./cell_app +``` + +CMake should be invoked from a separate `build` directory to keep +generated files isolated from the source tree. + +During configuration, module interfaces located in the `Cell/` directory are +converted into headers under `build/include/Cell`. These generated headers +allow interoperability with code that still relies on the traditional `#include` +mechanism. diff --git a/cell_framework/main.cpp b/cell_framework/main.cpp new file mode 100644 index 000000000..239dbf731 --- /dev/null +++ b/cell_framework/main.cpp @@ -0,0 +1,7 @@ +import Cell.Core; + +int main() { + Cell::Engine engine; + engine.run(); + return 0; +} diff --git a/scripts/generate_headers.cmake b/scripts/generate_headers.cmake new file mode 100644 index 000000000..5c8ae44a2 --- /dev/null +++ b/scripts/generate_headers.cmake @@ -0,0 +1,10 @@ +# Simple header generation from module interface files +file(GLOB MODULE_FILES "${CMAKE_CURRENT_LIST_DIR}/../cell_framework/Cell/*.ixx") +foreach(MFILE ${MODULE_FILES}) + get_filename_component(MNAME ${MFILE} NAME_WE) + set(HEADER "${GENERATED_INCLUDE_DIR}/Cell/${MNAME}.hpp") + file(MAKE_DIRECTORY "${GENERATED_INCLUDE_DIR}/Cell") + file(READ ${MFILE} CONTENTS) + string(REGEX REPLACE "export module ([A-Za-z0-9_.]+);" "#pragma once\n// Generated from module \1" CONTENTS "${CONTENTS}") + file(WRITE ${HEADER} "${CONTENTS}") +endforeach() diff --git a/scripts/install_zig.sh b/scripts/install_zig.sh new file mode 100755 index 000000000..6e7eb8ecc --- /dev/null +++ b/scripts/install_zig.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail +VERSION=0.14.1 +URL="https://ziglang.org/download/${VERSION}/zig-$(uname -m)-linux-${VERSION}.tar.xz" +TMP_DIR=$(mktemp -d) +trap 'rm -rf "$TMP_DIR"' EXIT +curl -L "$URL" -o "$TMP_DIR/zig.tar.xz" +mkdir -p "$TMP_DIR/extract" +tar -xf "$TMP_DIR/zig.tar.xz" -C "$TMP_DIR/extract" +sudo rm -rf /usr/local/zig +sudo mkdir -p /usr/local/zig +sudo cp -r "$TMP_DIR/extract"/*/ /usr/local/zig/ +sudo ln -sf /usr/local/zig/zig /usr/local/bin/zig diff --git a/src/agent.zig b/src/agent.zig index 4c90c1bf5..ff1031a0d 100644 --- a/src/agent.zig +++ b/src/agent.zig @@ -137,6 +137,7 @@ pub const personas = [_]Persona{ fn findPersona(name: []const u8) ?Persona { for (personas) |p| { + if (std.mem.eqlIgnoreCase(u8, p.name, name)) return p; if (std.mem.eql(u8, p.name, name)) return p; } return null; diff --git a/src/discord/gateway.zig b/src/discord/gateway.zig index c714b7a19..171808e2b 100644 --- a/src/discord/gateway.zig +++ b/src/discord/gateway.zig @@ -39,11 +39,12 @@ fn handleTextFrame(self: *DiscordBot, payload: []const u8) !void { const root = &parsed.value; const opv = root.object.get("op") orelse return; - const op = @intCast(types.OpCode, opv.integer); + const op: types.OpCode = @enumFromInt(opv.integer); switch (op) { .hello => if (root.object.get("d")) |data| { - const interval = data.object.get("heartbeat_interval")?.integer orelse return; + const iv = data.object.get("heartbeat_interval") orelse return; + const interval = iv.integer; try heartbeatLoop(self, @as(u32, @intCast(interval))); try identify(self); } else {}, @@ -55,7 +56,7 @@ fn heartbeatLoop(self: *DiscordBot, interval: u32) !void { var ws = self.ws.?; const buf = try self.allocator.alloc(u8, 32); defer self.allocator.free(buf); - var timer = std.time.Timer.start() catch return; + _ = std.time.Timer.start() catch return; while (true) { try std.json.stringify(.{ .op = @as(u8, @intCast(types.OpCode.heartbeat)), .d = null }, .{}, std.io.fixedBufferStream(buf).writer()); try ws.writeFrame(.{ .fin = true, .opcode = .text, .data = buf }); @@ -65,10 +66,10 @@ fn heartbeatLoop(self: *DiscordBot, interval: u32) !void { fn identify(self: *DiscordBot) !void { var ws = self.ws.?; - const identify = types.Identify{ .token = self.token, .intents = 1 << 15 }; + const id_payload = types.Identify{ .token = self.token, .intents = 1 << 15 }; var buf = std.ArrayList(u8).init(self.allocator); defer buf.deinit(); - try std.json.stringify(.{ .op = @as(u8, @intCast(types.OpCode.identify)), .d = identify }, .{}, buf.writer()); + try std.json.stringify(.{ .op = @as(u8, @intCast(types.OpCode.identify)), .d = id_payload }, .{}, buf.writer()); try ws.writeFrame(.{ .fin = true, .opcode = .text, .data = buf.items }); } @@ -79,7 +80,7 @@ fn fetchGatewayUrl(allocator: std.mem.Allocator, token: []const u8) ![]const u8 const auth_value = try std.fmt.allocPrint(allocator, "Bot {s}", .{token}); defer allocator.free(auth_value); - const headers = [_]std.http.Header{ .{ .name = "Authorization", .value = auth_value } }; + const headers = [_]std.http.Header{.{ .name = "Authorization", .value = auth_value }}; var buf = std.ArrayList(u8).init(allocator); defer buf.deinit(); const result = try client.fetch(.{ .location = .{ .url = "https://discord.com/api/v10/gateway" }, .extra_headers = &headers, .response_storage = .dynamic(&buf) }); diff --git a/src/discord/types.zig b/src/discord/types.zig index 97038c77c..b1fcecb02 100644 --- a/src/discord/types.zig +++ b/src/discord/types.zig @@ -6,7 +6,7 @@ pub const OpCode = enum(u8) { identify = 2, presence_update = 3, voice_state_update = 4, - resume = 6, + resume_session = 6, reconnect = 7, request_guild_members = 8, invalid_session = 9, diff --git a/src/engine/graphics.zig b/src/engine/graphics.zig new file mode 100644 index 000000000..9e3f6961c --- /dev/null +++ b/src/engine/graphics.zig @@ -0,0 +1,27 @@ +const std = @import("std"); + +pub const GraphicsBackend = enum { + vulkan, + metal, + direct3d12, + opengl, + webgpu, +}; + +pub const GraphicsDriver = struct { + backend: GraphicsBackend, + + pub fn init(backend: GraphicsBackend) GraphicsDriver { + return GraphicsDriver{ .backend = backend }; + } + + pub fn renderFrame(self: *GraphicsDriver) void { + // cross-platform rendering placeholder + _ = self; + } +}; + +test "GraphicsDriver init" { + const driver = GraphicsDriver.init(.vulkan); + try std.testing.expect(driver.backend == .vulkan); +} diff --git a/src/engine/mod.zig b/src/engine/mod.zig new file mode 100644 index 000000000..6d61d1c7d --- /dev/null +++ b/src/engine/mod.zig @@ -0,0 +1 @@ +pub const graphics = @import("graphics.zig"); diff --git a/src/localml.zig b/src/localml.zig index 91b5af7d2..daab2b7d5 100644 --- a/src/localml.zig +++ b/src/localml.zig @@ -211,9 +211,9 @@ pub fn main() !void { const data_contents = try std.fs.cwd().readFileAlloc(alloc, data_path, 1024 * 1024); defer alloc.free(data_contents); - var lines = std.mem.tokenize(u8, data_contents, "\n"); + var lines = std.mem.tokenizeScalar(u8, data_contents, '\n'); while (lines.next()) |line| { - var cols = std.mem.tokenize(u8, line, ","); + var cols = std.mem.tokenizeScalar(u8, line, ','); const x1 = try std.fmt.parseFloat(f64, cols.next() orelse continue); const x2 = try std.fmt.parseFloat(f64, cols.next() orelse continue); const label = try std.fmt.parseFloat(f64, cols.next() orelse continue); diff --git a/src/main.zig b/src/main.zig index 15f996888..dca48300d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -10,6 +10,7 @@ const gpu = @import("../zvim/gpu_renderer.zig"); const simd = @import("../zvim/simd_text.zig"); const lockfree = @import("lockfree.zig"); const platform = @import("platform.zig"); +const engine = @import("engine/mod.zig"); pub const Error = error{ EmptyText, @@ -91,37 +92,15 @@ pub const Abi = struct { pub fn main() !void { var args = std.process.args(); _ = args.next(); // exe name + var driver = engine.graphics.GraphicsDriver.init(.opengl); + driver.renderFrame(); if (args.next()) |arg| { if (std.mem.eql(u8, arg, "tui")) { const tui = @import("tui.zig"); try tui.run(); return; } else if (std.mem.eql(u8, arg, "discord")) { - const api = @import("discord/api.zig"); - const gw = @import("discord/gateway.zig"); - const bot = @import("discord_bot.zig"); - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - const token = std.process.getEnvVarOwned(allocator, "DISCORD_TOKEN") catch { - std.log.err("DISCORD_TOKEN environment variable not set", .{}); - return; - }; - defer allocator.free(token); - - const channel = args.next() orelse { - std.log.err("channel id required", .{}); - return; - }; - - var bot = gw.DiscordBot.init(allocator, token); - defer bot.deinit(); - // Non-blocking send using REST API - try api.postMessage(allocator, token, channel, "Hello from Zig!"); - // Connect to gateway in blocking mode (example only) - // try bot.connect(); - try bot.postMessage(allocator, token, channel, "Hello from Zig!"); + std.log.err("discord feature not available", .{}); return; } } @@ -130,7 +109,7 @@ pub fn main() !void { .text = "example input", .values = &[_]usize{ 1, 2, 3, 4 }, }; - const res = Abi.process(req); + const res = try Abi.process(req); const stdout = std.io.getStdOut().writer(); try stdout.print("{s}: {d}\n", .{ res.message, res.result }); } diff --git a/src/mlai/mlai.zig b/src/mlai/mlai.zig new file mode 100644 index 000000000..6b997ade7 --- /dev/null +++ b/src/mlai/mlai.zig @@ -0,0 +1,44 @@ +const std = @import("std"); +const agent = @import("../agent.zig"); +const wdbx = @import("mlai/wdbx/db.zig"); + +pub const MLAISystem = struct { + allocator: std.mem.Allocator, + db: wdbx.Database, + + pub fn init(alloc: std.mem.Allocator, cfg: wdbx.Config) !MLAISystem { + return MLAISystem{ + .allocator = alloc, + .db = try wdbx.Database.init(alloc, cfg), + }; + } + + pub fn deinit(self: *MLAISystem) void { + self.db.deinit(); + } + + pub fn processRequest(self: *MLAISystem, query: []const u8) ![]u8 { + const persona = router(query); + var response = try agent.respond(persona, query, self.allocator); + errdefer self.allocator.free(response); + try self.db.storeInteraction(query, response, persona); + return response; + } +}; + +pub fn router(query: []const u8) agent.PersonaType { + if (std.mem.indexOf(u8, query, "help") != null) return .EmpatheticAnalyst; + if (std.mem.indexOf(u8, query, "explain") != null) return .DirectExpert; + return .AdaptiveModerator; +} + +pub test "process request stores data" { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + var system = try MLAISystem.init(gpa.allocator(), .{ .shard_count = 2 }); + defer system.deinit(); + const reply = try system.processRequest("help me"); + defer gpa.allocator().free(reply); + const e = system.db.retrieve("help me") orelse return error.NotFound; + try std.testing.expectEqualStrings(e.value, reply); +} diff --git a/src/mlai/wdbx/db.zig b/src/mlai/wdbx/db.zig new file mode 100644 index 000000000..5dc96cb6e --- /dev/null +++ b/src/mlai/wdbx/db.zig @@ -0,0 +1,111 @@ +const std = @import("std"); +const agent = @import("../../agent.zig"); + +pub const Config = struct { + shard_count: u32 = 3, +}; + +const prime_numbers = [_]u64{31, 37, 43, 47, 53, 59, 61, 67, 71, 73}; + +pub fn primeHash(data: []const u8, seed: u64) u64 { + var hash: u64 = seed | 1; + for (data) |b| { + hash ^= @intCast(u64, b); + hash *= 0x9e3779b97f4a7c15; + hash = (hash << 7) | (hash >> 57); + } + return hash; +} + +pub fn calculateShard(key: []const u8, prime: u64) usize { + return @intCast(usize, primeHash(key, 0) % prime); +} + +pub const Entry = struct { + key: []u8, + value: []u8, + persona: agent.PersonaType, + version: u64, +}; + +pub const Shard = struct { + id: u64, + entries: std.ArrayList(Entry), + + pub fn init(alloc: std.mem.Allocator, id: u64) !Shard { + return Shard{ .id = id, .entries = std.ArrayList(Entry).init(alloc) }; + } + + pub fn store(self: *Shard, key: []const u8, value: []const u8, p: agent.PersonaType) !void { + try self.entries.append(.{ + .key = try self.entries.allocator.dupe(u8, key), + .value = try self.entries.allocator.dupe(u8, value), + .persona = p, + .version = @as(u64, @intCast(std.time.nanoTimestamp())), + }); + } + + pub fn retrieve(self: *Shard, key: []const u8) ?Entry { + var i: usize = self.entries.items.len; + while (i > 0) { + i -= 1; + if (std.mem.eql(u8, self.entries.items[i].key, key)) { + return self.entries.items[i]; + } + } + return null; + } +}; + +pub const Database = struct { + allocator: std.mem.Allocator, + shards: []Shard, + prime: u64, + + pub fn init(alloc: std.mem.Allocator, cfg: Config) !Database { + const count = cfg.shard_count; + if (count == 0 or count > prime_numbers.len) return error.InvalidShardCount; + var shards = try alloc.alloc(Shard, count); + for (shards, 0..) |*s, i| { + s.* = try Shard.init(alloc, prime_numbers[i]); + } + return Database{ .allocator = alloc, .shards = shards, .prime = prime_numbers[count - 1] }; + } + + pub fn deinit(self: *Database) void { + for (self.shards) |*s| { + for (s.entries.items) |e| { + self.allocator.free(e.key); + self.allocator.free(e.value); + } + s.entries.deinit(); + } + self.allocator.free(self.shards); + } + + fn shardIndex(self: *Database, key: []const u8) usize { + return calculateShard(key, self.prime) % self.shards.len; + } + + pub fn storeInteraction(self: *Database, req: []const u8, resp: []const u8, p: agent.PersonaType) !void { + const idx = self.shardIndex(req); + try self.shards[idx].store(req, resp, p); + } + + pub fn retrieve(self: *Database, key: []const u8) ?Entry { + const idx = self.shardIndex(key); + return self.shards[idx].retrieve(key); + } +}; + +pub const WDBXError = error{InvalidShardCount}; + +pub test "basic store and retrieve" { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + var db = try Database.init(gpa.allocator(), .{ .shard_count = 2 }); + defer db.deinit(); + try db.storeInteraction("hello", "world", .EmpatheticAnalyst); + const e = db.retrieve("hello") orelse return error.NotFound; + try std.testing.expectEqualStrings(e.value, "world"); +} diff --git a/src/platform.zig b/src/platform.zig index 8b28b37db..7f6075c76 100644 --- a/src/platform.zig +++ b/src/platform.zig @@ -1,11 +1,14 @@ //! Platform-specific optimizations and abstractions +const std = @import("std"); +const builtin = @import("builtin"); + pub const PlatformLayer = struct { /// iOS-specific optimizations for a-Shell pub const iOS = struct { const max_memory = 256 * 1024 * 1024; // 256MB limit const max_file_handles = 256; - + pub fn init() !void { // Set up iOS-specific memory limits if (builtin.os.tag == .ios) { @@ -13,24 +16,15 @@ pub const PlatformLayer = struct { const dispatch = @cImport({ @cInclude("dispatch/dispatch.h"); }); - - dispatch.dispatch_source_set_event_handler( - dispatch.dispatch_source_create( - dispatch.DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, - 0, - dispatch.DISPATCH_MEMORYPRESSURE_WARN, - dispatch.dispatch_get_main_queue() - ), - struct { - fn handler() callconv(.C) void { - // Aggressive memory cleanup - _ = gpa.collectGarbage(); - } - }.handler - ); + + dispatch.dispatch_source_set_event_handler(dispatch.dispatch_source_create(dispatch.DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, dispatch.DISPATCH_MEMORYPRESSURE_WARN, dispatch.dispatch_get_main_queue()), struct { + fn handler() callconv(.C) void { + // Aggressive memory cleanup placeholder + } + }.handler); } } - + pub fn openFile(path: []const u8) !std.fs.File { // iOS sandbox restrictions const allowed_prefixes = [_][]const u8{ @@ -38,72 +32,71 @@ pub const PlatformLayer = struct { "~/tmp/", "/private/var/mobile/", }; - + for (allowed_prefixes) |prefix| { if (std.mem.startsWith(u8, path, prefix)) { return std.fs.cwd().openFile(path, .{}); } } - + return error.SandboxViolation; } }; - + /// Windows-specific console optimizations pub const Windows = struct { + pub const ConPTY = struct { + handle: *anyopaque, + input: *anyopaque, + output: *anyopaque, + }; pub fn enableAnsiColors() !void { const kernel32 = @cImport({ @cInclude("windows.h"); }); - + const stdout_handle = kernel32.GetStdHandle(kernel32.STD_OUTPUT_HANDLE); var mode: kernel32.DWORD = 0; - + if (kernel32.GetConsoleMode(stdout_handle, &mode) == 0) { return error.GetConsoleModeFailed; } - + mode |= kernel32.ENABLE_VIRTUAL_TERMINAL_PROCESSING; mode |= kernel32.ENABLE_PROCESSED_OUTPUT; - + if (kernel32.SetConsoleMode(stdout_handle, mode) == 0) { return error.SetConsoleModeFailed; } } - + pub fn createConPTY(cols: u16, rows: u16) !ConPTY { const kernel32 = @cImport({ @cInclude("windows.h"); @cInclude("consoleapi.h"); }); - - var size = kernel32.COORD{ .X = cols, .Y = rows }; + + const size = kernel32.COORD{ .X = cols, .Y = rows }; var input_pipe: kernel32.HANDLE = undefined; var output_pipe: kernel32.HANDLE = undefined; var pty: kernel32.HPCON = undefined; - + // Create pipes if (kernel32.CreatePipe(&input_pipe, null, null, 0) == 0) { return error.CreatePipeFailed; } - + if (kernel32.CreatePipe(null, &output_pipe, null, 0) == 0) { return error.CreatePipeFailed; } - + // Create pseudo console - const hr = kernel32.CreatePseudoConsole( - size, - input_pipe, - output_pipe, - 0, - &pty - ); - + const hr = kernel32.CreatePseudoConsole(size, input_pipe, output_pipe, 0, &pty); + if (hr != kernel32.S_OK) { return error.CreatePseudoConsoleFailed; } - + return ConPTY{ .handle = pty, .input = input_pipe, @@ -111,52 +104,52 @@ pub const PlatformLayer = struct { }; } }; - + /// Linux io_uring for maximum async I/O performance pub const Linux = struct { pub const AsyncIO = struct { ring: std.os.linux.io_uring, submission_queue: []std.os.linux.io_uring_sqe, completion_queue: []std.os.linux.io_uring_cqe, - + pub fn init(queue_depth: u13) !AsyncIO { var ring: std.os.linux.io_uring = undefined; const params = std.os.linux.io_uring_params{}; - + try std.os.linux.io_uring_setup(queue_depth, ¶ms, &ring); - + return AsyncIO{ .ring = ring, .submission_queue = undefined, // Mapped separately .completion_queue = undefined, }; } - + pub fn readFile(self: *AsyncIO, path: []const u8, buffer: []u8) !usize { const fd = try std.os.open(path, .{ .ACCMODE = .RDONLY }, 0); defer std.os.close(fd); - + // Get submission queue entry const sqe = try self.getSQE(); std.os.linux.io_uring_prep_read(sqe, fd, buffer.ptr, buffer.len, 0); sqe.user_data = 1; - + // Submit and wait _ = try std.os.linux.io_uring_submit(&self.ring); - + var cqe: *std.os.linux.io_uring_cqe = undefined; _ = try std.os.linux.io_uring_wait_cqe(&self.ring, &cqe); defer std.os.linux.io_uring_cqe_seen(&self.ring, cqe); - + if (cqe.res < 0) { return error.ReadFailed; } - + return @intCast(cqe.res); } }; }; - + /// macOS unified memory optimizations pub const macOS = struct { pub fn createUnifiedBuffer(size: usize) ![]u8 { @@ -164,28 +157,18 @@ pub const PlatformLayer = struct { @cInclude("mach/mach.h"); @cInclude("mach/vm_map.h"); }); - + var address: mach.vm_address_t = 0; - const kr = mach.vm_allocate( - mach.mach_task_self(), - &address, - size, - mach.VM_FLAGS_ANYWHERE - ); - + const kr = mach.vm_allocate(mach.mach_task_self(), &address, size, mach.VM_FLAGS_ANYWHERE); + if (kr != mach.KERN_SUCCESS) { return error.VmAllocateFailed; } - + // Mark as purgeable for memory pressure handling var state: mach.vm_purgable_t = mach.VM_PURGABLE_NONVOLATILE; - _ = mach.vm_purgable_control( - mach.mach_task_self(), - address, - mach.VM_PURGABLE_SET_STATE, - &state - ); - + _ = mach.vm_purgable_control(mach.mach_task_self(), address, mach.VM_PURGABLE_SET_STATE, &state); + return @as([*]u8, @ptrFromInt(address))[0..size]; } }; diff --git a/src/tui.zig b/src/tui.zig index 789724809..47386e749 100644 --- a/src/tui.zig +++ b/src/tui.zig @@ -28,7 +28,6 @@ pub const Command = union(enum) { }; pub const Term = struct { - orig_term: ?std.os.termios = null, buf: [1024]u8 = undefined, stdin: std.fs.File, stdout: std.fs.File, @@ -37,17 +36,10 @@ pub const Term = struct { var t = Term{ .stdin = std.io.getStdIn(), .stdout = std.io.getStdOut(), - .orig_term = null, }; if (std.posix.isatty(0)) { - const tio = std.os.tcgetattr(0) catch return TuiError.TerminalError; - t.orig_term = tio; - var raw = tio; - raw.lflag &= ~(std.os.termiosFlags.ECHO | std.os.termiosFlags.ICANON); - raw.c_cc[std.os.VMIN] = 1; - raw.c_cc[std.os.VTIME] = 0; - std.os.tcsetattr(0, std.os.TCSANOW, &raw) catch return TuiError.TerminalError; + // Terminal configuration skipped in this minimal build } try t.clearScreen(); @@ -55,9 +47,6 @@ pub const Term = struct { } pub fn deinit(self: *Term) void { - if (self.orig_term) |orig| { - _ = std.os.tcsetattr(0, std.os.TCSANOW, &orig) catch {}; - } self.clearScreen() catch {}; } @@ -77,7 +66,7 @@ pub const Term = struct { pub fn parseCommand(self: Term, line: []const u8) TuiError!Command { _ = self; - var it = std.mem.tokenize(u8, line, " "); + var it = std.mem.tokenizeScalar(u8, line, ' '); const cmd = it.next() orelse return TuiError.InvalidCommand; if (std.mem.eql(u8, cmd, "help")) {