diff --git a/src/Package.zig b/src/Package.zig index d891d26..422f0c9 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -27,14 +27,75 @@ const Package = @This(); /// Rules: /// - no element in array or hashmap are allowed to hold pointers (ArrayList and HashMaps are also pointers). pub const State = struct { + /// Maps each unique `Package.ID` to its entry in `packages`. package_table: std.AutoArrayHashMapUnmanaged(Id, Package.Idx) = .empty, + /// Owns the interned strings referenced by packages and dependencies. string_state: string.State = .empty, + /// Append-only package storage; duplicate packages may share an ID. packages: std.MultiArrayList(Package) = .empty, + /// Contiguous storage addressed by each package's dependency ranges. dependencies: std.ArrayList(Dependency) = .empty, pub const empty = State{}; - // TODO: make wrapper api to minimize fault use. + pub const DependencyKind = enum { compile, runtime }; + + pub const Entry = struct { + package: Package, + }; + + pub const Iterator = struct { + packages_slice: std.MultiArrayList(Package).Slice, + table_iterator: std.AutoArrayHashMapUnmanaged(Id, Package.Idx).Iterator, + + pub fn next(self: *Iterator) ?Package { + const entry = self.table_iterator.next() orelse return null; + assert(entry.key_ptr.* != .none); + const index = @intFromEnum(entry.value_ptr.*); + assert(index < self.packages_slice.len); + const package = self.packages_slice.get(index); + assert(package.id == entry.key_ptr.*); + return package; + } + }; + + pub fn get(self: *const State, id: Id) ?Package { + assert(id != .none); + const index = self.package_table.get(id) orelse return null; + const package_index = @intFromEnum(index); + assert(package_index < self.packages.len); + const package = self.packages.get(package_index); + assert(package.id == id); + return package; + } + + pub fn getDependencies(self: *const State, package: Package, kind: DependencyKind) []const Dependency { + const deps = switch (kind) { + .compile => package.compile_deps, + .runtime => package.runtime_deps, + }; + const start: usize = deps.start; + const dependency_count: usize = deps.count; + assert(package.id != .none); + assert(start <= self.dependencies.items.len); + assert(dependency_count <= self.dependencies.items.len - start); + return self.dependencies.items[start..][0..dependency_count]; + } + + pub fn count(self: *const State) usize { + assert(self.package_table.count() <= self.packages.len); + return self.package_table.count(); + } + + /// Iterate over Packages. + /// It is not safe to mofify state while iterating + pub fn iterator(self: *const State) Iterator { + assert(self.package_table.count() <= self.packages.len); + return .{ + .packages_slice = self.packages.slice(), + .table_iterator = self.package_table.iterator(), + }; + } pub fn deinit(self: *State, gpa: Allocator) void { self.package_table.deinit(gpa); @@ -50,11 +111,15 @@ pub const Dependency = struct { }; /// Unique hash(Id) of a Package by hashing OS, cpu Arch, manifests and dependecies manifests. pub const Id = String; -/// idx into State.packages +/// Index into State.packages pub const Idx = enum(u32) { _ }; +/// Unique package id generated from hashing build input +id: Id = .none, +/// name of Package name: String, version: std.SemanticVersion, +/// Lua stack index of pkg from manifest lua_idx: i32, desc: String, homepage: String, @@ -117,7 +182,7 @@ pub fn init( lua.pop(1); if (!std.mem.eql(u8, pkg_name, name.slice(&state.string_state))) { - log.err("Name differs", .{}); + log.err("Package name differs from expected name '{s}', got {s}", .{ pkg_name, name.slice(&state.string_state) }); return error.WrontPackageName; } @@ -220,6 +285,7 @@ pub fn init( } pop_count += 1; // deps lua.pop(pop_count); + lua.pop(1); // package return .{ .name = name, @@ -250,11 +316,9 @@ pub fn collect( var blake3: std.crypto.hash.Blake3 = .init(.{ .key = null }); try state.packages.ensureUnusedCapacity(gpa, 1); + const package = try Package.init(io, gpa, state, packa_dir, "core", name, lua, &blake3); const pkg_idx = state.packages.addOneAssumeCapacity(); - state.packages.set( - pkg_idx, - try .init(io, gpa, state, packa_dir, "core", name, lua, &blake3), - ); + state.packages.set(pkg_idx, package); state.packages.items(.install)[pkg_idx] = install; const runtime_deps: Deps = state.packages.items(.runtime_deps)[pkg_idx]; @@ -289,5 +353,6 @@ pub fn collect( assert(names[@intFromEnum(gop.value_ptr.*)] == names[pkg_idx]); } gop.value_ptr.* = @enumFromInt(pkg_idx); + state.packages.items(.id)[pkg_idx] = key_string; return key_string; } diff --git a/src/actions/build_package.zig b/src/actions/build_package.zig index 20cd387..a17591a 100644 --- a/src/actions/build_package.zig +++ b/src/actions/build_package.zig @@ -45,16 +45,14 @@ pub fn build(io: Io, gpa: Allocator, arena: Allocator, env: *std.process.Environ const pkg_id = try Package.collect(io, arena, &state, packa_dir, args.package_name, &lua, true); // TODO: fetch and install deps - const pkg_idx = state.package_table.get(pkg_id) orelse return error.FailedToCollectPackage; - const pkg = state.packages.get(@intFromEnum(pkg_idx)); + const pkg = state.get(pkg_id) orelse return error.FailedToCollectPackage; const pkg_key = pkg_id.slice(&state.string_state); const pkg_name = pkg.name.slice(&state.string_state); - const compile_deps = state.dependencies.items[pkg.compile_deps.start..][0..pkg.compile_deps.count]; + const compile_deps = state.getDependencies(pkg, .compile); for (compile_deps) |dependency| { - const dep_idx = state.package_table.get(dependency.pkg_id) orelse return error.FailedToCollectPackage; - const dep = state.packages.get(@intFromEnum(dep_idx)); + const dep = state.get(dependency.pkg_id) orelse return error.FailedToCollectPackage; const dep_name = dep.name.slice(&state.string_state); const dep_key = dependency.pkg_id.slice(&state.string_state); const store_path = try bufPrint(&print_buf, "/opt/packa/store/{s}-{f}-{s}", .{ @@ -521,7 +519,7 @@ fn luaDep(state: ?*zlua.LuaState) callconv(.c) c_int { const arena = arena_impl.allocator(); - const pkg_idx = ctx.pkg_state.package_table.get(ctx.pkg_id) orelse { + const pkg = ctx.pkg_state.get(ctx.pkg_id) orelse { lua.pushNil(); _ = lua.pushLString(std.fmt.allocPrint(arena, "could not find idx for package id {s}, this should not happen", .{ ctx.pkg_id.slice(string_state), @@ -531,9 +529,8 @@ fn luaDep(state: ?*zlua.LuaState) callconv(.c) c_int { }; const dep_id = get_dep_id: { - const pkg_comp_deps: Package.Deps = ctx.pkg_state.packages.items(.compile_deps)[@intFromEnum(pkg_idx)]; - for (0..pkg_comp_deps.count) |i| { - const pkg_dep: Package.Dependency = ctx.pkg_state.dependencies.items[pkg_comp_deps.start..][i]; + const pkg_compile_deps = ctx.pkg_state.getDependencies(pkg, .compile); + for (pkg_compile_deps) |pkg_dep| { if (std.mem.eql(u8, dep_name, pkg_dep.name.slice(string_state))) { break :get_dep_id pkg_dep.pkg_id; } @@ -546,15 +543,13 @@ fn luaDep(state: ?*zlua.LuaState) callconv(.c) c_int { return 2; }; - const dep_idx = ctx.pkg_state.package_table.get(dep_id) orelse { + const dep = ctx.pkg_state.get(dep_id) orelse { lua.pushNil(); _ = lua.pushLString(std.fmt.allocPrint(arena, "failed to get package idx for dep id '{s}', this should'nt happen", .{ dep_id.slice(string_state), }) catch @panic("OOM")); return 2; }; - const dep = ctx.pkg_state.packages.get(@intFromEnum(dep_idx)); - const store_path = std.fmt.allocPrint(arena, "/opt/packa/store/{s}-{f}-{s}", .{ dep.name.slice(string_state), dep.version, dep_id.slice(string_state)[0..32], }) catch @panic("OOM"); diff --git a/src/actions/info.zig b/src/actions/info.zig index 722e795..0ce2435 100644 --- a/src/actions/info.zig +++ b/src/actions/info.zig @@ -79,7 +79,7 @@ fn printInfo( pkg_id: Package.Id, state: *const Package.State, ) !void { - const pkg = state.packages.get(@intFromEnum(state.package_table.get(pkg_id).?)); + const pkg = state.get(pkg_id) orelse return error.MissingPackageIdx; try t.setColor(.bold); try t.writer.print("{s}-{f}\n", .{ pkg.name.slice(&state.string_state), pkg.version }); @@ -130,9 +130,9 @@ fn printDeps( pipes: u64, path_buf: []u8, ) !void { - const pkg = state.packages.get(@intFromEnum(state.package_table.get(pkg_id).?)); - const comp_deps = state.dependencies.items[pkg.compile_deps.start..][0..pkg.compile_deps.count]; - const run_deps = state.dependencies.items[pkg.runtime_deps.start..][0..pkg.runtime_deps.count]; + const pkg = state.get(pkg_id) orelse return error.MissingPackageIdx; + const comp_deps = state.getDependencies(pkg, .compile); + const run_deps = state.getDependencies(pkg, .runtime); const total = comp_deps.len + run_deps.len; for (0..total) |i| { @@ -140,7 +140,7 @@ fn printDeps( const dep = if (is_comp) comp_deps[i] else run_deps[i - comp_deps.len]; const is_last = (i == total - 1); - const dep_pkg = state.packages.get(@intFromEnum(state.package_table.get(dep.pkg_id).?)); + const dep_pkg = state.get(dep.pkg_id) orelse return error.MissingPackageIdx; for (0..level) |l| { const pipe = if ((pipes >> @intCast(l)) & 1 == 1) "│ " else " "; diff --git a/src/actions/install.zig b/src/actions/install.zig index 2f2baae..4b7ad05 100644 --- a/src/actions/install.zig +++ b/src/actions/install.zig @@ -90,8 +90,7 @@ pub fn install( try fetchPackages(io, gpa, cache_dir, &state, &pub_key, progress); for (package_ids.items) |id| { - const idx = state.package_table.get(id) orelse return error.MissingPackageIdx; - const pkg = state.packages.get(@intFromEnum(idx)); + const pkg = state.get(id) orelse return error.MissingPackageIdx; // TODO: install runtime deps first recursively // for (pkg.runtime_deps.items) |dep_id| {} // @@ -110,7 +109,7 @@ fn fetchPackages( pub_key: *const minizign.PublicKey, progress: std.Progress.Node, ) !void { - const package_count = resolved.package_table.count(); + const package_count = resolved.count(); var fetch_progress = progress.start("fetching", package_count); defer fetch_progress.end(); @@ -125,21 +124,18 @@ fn fetchPackages( var path_buf: [Io.Dir.max_path_bytes]u8 = undefined; var fetch_count: usize = 0; - const pkg_slice = resolved.packages.slice(); - var it = resolved.package_table.iterator(); - while (it.next()) |entry| { - const pkg_id = entry.key_ptr.*; - const pkg_idx = entry.value_ptr.*; - const name = pkg_slice.items(.name)[@intFromEnum(pkg_idx)]; - const version = pkg_slice.items(.version)[@intFromEnum(pkg_idx)]; + var it = resolved.iterator(); + while (it.next()) |pkg| { + const pkg_id = pkg.id; + const name = pkg.name; + const version = pkg.version; const path = try std.fmt.bufPrint(&path_buf, "{s}-{f}-{s}.tar.zst", .{ name.slice(&resolved.string_state), version, pkg_id.slice(&resolved.string_state)[0..32], }); cahce_dir.access(io, path, .{}) catch { - // try fetchPackage(io, gpa, pkg_id, pkg_idx, pkg_slice, &resolved.string_state, &queue, pub_key, fetch_progress); group.async(io, fetchPackage, .{ - io, gpa, pkg_id, pkg_idx, pkg_slice, &resolved.string_state, &queue, pub_key, fetch_progress, + io, gpa, pkg_id, pkg, &resolved.string_state, &queue, pub_key, fetch_progress, }); fetch_count += 1; continue; @@ -165,16 +161,15 @@ fn fetchPackage( io: Io, gpa: Allocator, pkg_id: Package.Id, - pkg_idx: Package.Idx, - packages_slice: std.MultiArrayList(Package).Slice, + pkg: Package, string_state: *const string.State, queue: *Io.Queue(anyerror!void), pub_key: *const minizign.PublicKey, progress: std.Progress.Node, ) Io.Cancelable!void { - const name = packages_slice.items(.name)[@intFromEnum(pkg_idx)]; + const name = pkg.name; const name_slice = name.slice(string_state); - const version = packages_slice.items(.version)[@intFromEnum(pkg_idx)]; + const version = pkg.version; const pkg_id_slice = pkg_id.slice(string_state); defer std.debug.print("finished downloading {s}\n", .{name_slice}); diff --git a/src/string.zig b/src/string.zig index b151c6f..27bf5b8 100644 --- a/src/string.zig +++ b/src/string.zig @@ -14,6 +14,7 @@ pub const State = struct { state.string_table.deinit(gpa); } + /// Internet String pub const String = enum(u32) { none = std.math.maxInt(u32), _,