aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-12-18 17:50:35 -0800
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-12-18 17:50:35 -0800
commit1c9131a5d71f601baabef9460b8e396b2790f3f1 (patch)
tree02f172c1c648ea2c288dd4185c3678353c24807d /src
parentfb758a32e1f28bbcd91616df589f17a7335e41ba (diff)
downloadbun-1c9131a5d71f601baabef9460b8e396b2790f3f1.tar.gz
bun-1c9131a5d71f601baabef9460b8e396b2790f3f1.tar.zst
bun-1c9131a5d71f601baabef9460b8e396b2790f3f1.zip
Not using hop!
Diffstat (limited to 'src')
-rw-r--r--src/global.zig3
-rw-r--r--src/hop/hop.zig324
-rw-r--r--src/hop/schema.zig511
-rw-r--r--src/install/install.zig241
-rw-r--r--src/libarchive/libarchive.zig174
5 files changed, 89 insertions, 1164 deletions
diff --git a/src/global.zig b/src/global.zig
index 3407e837b..08cc77fab 100644
--- a/src/global.zig
+++ b/src/global.zig
@@ -1,8 +1,7 @@
const std = @import("std");
pub const Environment = @import("env.zig");
-const use_mimalloc = false;
-
+const use_mimalloc = !Environment.isTest;
pub const default_allocator: *std.mem.Allocator = if (!use_mimalloc)
std.heap.c_allocator
diff --git a/src/hop/hop.zig b/src/hop/hop.zig
deleted file mode 100644
index 90def6dc6..000000000
--- a/src/hop/hop.zig
+++ /dev/null
@@ -1,324 +0,0 @@
-const std = @import("std");
-const C = @import("../c.zig");
-const Schema = @import("./schema.zig");
-const Hop = Schema.Hop;
-const Environment = @import("../env.zig");
-
-const string = []const u8;
-
-pub fn cmpStringsAsc(ctx: void, a: string, b: string) bool {
- return std.mem.order(u8, a, b) == .lt;
-}
-
-pub fn cmpStringsDesc(ctx: void, a: string, b: string) bool {
- return std.mem.order(u8, a, b) == .gt;
-}
-
-const sort_asc = std.sort.asc(u8);
-const sort_desc = std.sort.desc(u8);
-
-pub fn sortAsc(in: []string) void {
- std.sort.sort([]const u8, in, {}, cmpStringsAsc);
-}
-
-pub fn sortDesc(in: []string) void {
- std.sort.sort([]const u8, in, {}, cmpStringsDesc);
-}
-
-pub const Library = struct {
- pub const magic_bytes = "#!/usr/bin/env hop\n\n";
- const Header = [magic_bytes.len + 5]u8;
- pub usingnamespace Schema.Hop;
-
- archive: Hop.Archive,
- allocator: *std.mem.Allocator,
- metadata_bytes: []u8,
- fd: ?std.os.fd_t,
-
- pub const Builder = struct {
- allocator: *std.mem.Allocator,
- files: std.ArrayListUnmanaged(Hop.File),
- directories: std.ArrayListUnmanaged(Hop.Directory),
- metadata_bytes: std.ArrayListUnmanaged(u8),
- destination: std.fs.File = undefined,
-
- pub fn init(allocator: *std.mem.Allocator) Builder {
- return Builder{
- .allocator = allocator,
- .metadata_bytes = .{},
- .directories = .{},
- .files = std.ArrayListUnmanaged(Hop.File){},
- };
- }
-
- pub fn start(this: *Builder, file: std.fs.File) !void {
- this.destination = file;
- try file.seekTo(0);
-
- // Write the header with 0 set as the content offset
- try file.writeAll(magic_bytes ++ [5]u8{ 0, 0, 0, 0, '\n' });
- }
-
- const FileSorter = struct {
- metadata: []const u8,
- pub fn sortByName(this: FileSorter, lhs: Hop.File, rhs: Hop.File) bool {
- return std.mem.order(u8, this.metadata[lhs.name.off..][0..lhs.name.len], this.metadata[rhs.name.off..][0..rhs.name.len]) == .lt;
- }
- };
-
- const DirSorter = struct {
- metadata: []const u8,
- pub fn sortByName(this: DirSorter, lhs: Hop.Directory, rhs: Hop.Directory) bool {
- return std.mem.order(u8, this.metadata[lhs.name.off..][0..lhs.name.len], this.metadata[rhs.name.off..][0..rhs.name.len]) == .lt;
- }
- };
-
- pub fn done(this: *Builder) !Hop.Archive {
- const metadata_offset = @truncate(u32, try this.destination.getPos());
-
- {
- var sorter = FileSorter{
- .metadata = this.metadata_bytes.items,
- };
-
- std.sort.sort(Hop.File, this.files.items, sorter, FileSorter.sortByName);
- }
-
- {
- var sorter = DirSorter{
- .metadata = this.metadata_bytes.items,
- };
-
- std.sort.sort(Hop.Directory, this.directories.items, sorter, DirSorter.sortByName);
- }
-
- var name_hashes = try this.allocator.alloc(u32, this.files.items.len);
-
- for (this.files.items) |file, i| {
- name_hashes[i] = file.name_hash;
- }
-
- var archive = Hop.Archive{
- .version = 1,
- .files = this.files.items,
- .directories = this.directories.items,
- .name_hashes = name_hashes,
- .content_offset = metadata_offset,
- .metadata = this.metadata_bytes.items,
- };
-
- var schema_writer = Schema.FileWriter.init(this.destination);
- try archive.encode(&schema_writer);
-
- var header: Header = undefined;
- header[0..magic_bytes.len].* = magic_bytes.*;
- std.mem.writeIntNative(u32, header[magic_bytes.len..][0..4], metadata_offset);
- header[magic_bytes.len..][4] = '\n';
- try this.destination.pwriteAll(&header, 0);
-
- _ = C.fchmod(
- this.destination.handle,
- // chmod 777
- 0000010 | 0000100 | 0000001 | 0001000 | 0000040 | 0000004 | 0000002 | 0000400 | 0000200 | 0000020,
- );
-
- return archive;
- }
-
- pub fn appendMetadata(this: *Builder, bytes: []const u8) !Hop.StringPointer {
- const off = @truncate(u32, this.metadata_bytes.items.len);
-
- // Keep a null ptr at the end of the metadata so that C APIs expecting sentinel ptrs work without copying
- try this.metadata_bytes.appendSlice(this.allocator, bytes);
- try this.metadata_bytes.append(this.allocator, 0);
- return Hop.StringPointer{
- .off = off,
- .len = @truncate(u32, bytes.len),
- };
- }
-
- pub fn appendContent(this: *Builder, bytes: []const u8) !Hop.StringPointer {
- const off = try this.destination.getPos();
- try this.destination.writeAll(bytes);
- return Hop.StringPointer{
- .off = off,
- .len = bytes.len,
- };
- }
-
- pub fn appendContentFromDisk(this: *Builder, name: []const u8, in: std.fs.File) !void {
- var stat = try in.stat();
-
- _ = try this.destination.write("\n");
- const off_in = try this.destination.getPos();
- const written = try std.os.copy_file_range(in.handle, 0, this.destination.handle, off_in, stat.size, 0);
- try this.destination.seekTo(off_in + written);
- const end = try this.destination.getPos();
- try this.appendFileMetadataFromDisk(name, off_in, end, stat);
- try this.destination.writeAll(&[_]u8{0});
- }
-
- pub fn appendDirectoryFromDisk(this: *Builder, name: []const u8, in: std.fs.Dir) !void {
- var stat = try std.os.fstatat(in.fd, name, 0);
-
- try this.appendDirMetadataFromDisk(name, stat);
- }
-
- pub fn appendFileMetadataFromDisk(this: *Builder, name_buf: []const u8, start_pos: u64, end_pos: u64, stat: std.fs.File.Stat) !void {
- const name = try this.appendMetadata(name_buf);
- try this.files.append(
- this.allocator,
- Hop.File{
- .name = name,
- .name_hash = @truncate(u32, std.hash.Wyhash.hash(0, name_buf)),
- .data = Schema.Hop.StringPointer{ .off = @truncate(u32, start_pos), .len = @truncate(u32, end_pos - start_pos) },
- .chmod = @truncate(u32, stat.mode),
- .mtime = @truncate(u32, @intCast(u128, @divFloor(stat.mtime, std.time.ns_per_s))),
- .ctime = @truncate(u32, @intCast(u128, @divFloor(stat.ctime, std.time.ns_per_s))),
- },
- );
- }
-
- pub fn appendDirMetadataFromDisk(this: *Builder, name_buf: []const u8, stat: std.fs.File.Stat) !void {
- const name = try this.appendMetadata(name_buf);
- try this.directories.append(
- this.allocator,
- Hop.Directory{
- .name = name,
- .name_hash = @truncate(u32, std.hash.Wyhash.hash(0, name_buf)),
- .chmod = @truncate(u32, stat.mode),
- },
- );
- }
-
- pub fn appendFileMetadata(this: *Builder, name_buf: []const u8, meta: Hop.File) !void {
- const name = try this.appendMetadata(name_buf);
- try this.files.append(
- this.allocator,
- Hop.File{
- .name = name,
- .name_hash = @truncate(u32, std.hash.Wyhash.hash(0, name_buf)),
- .data = meta.data,
- .chmod = meta.chmod,
- .mtime = meta.mtime,
- .ctime = meta.ctime,
- },
- );
- }
-
- pub fn appendDirectoryRecursively(this: *Builder, dir: std.fs.Dir) !void {
- var walker = try dir.walk(this.allocator);
- defer walker.deinit();
- while (try walker.next()) |entry_| {
- const entry: std.fs.Dir.Walker.WalkerEntry = entry_;
-
- switch (entry.kind) {
- .Directory => {
- try this.appendDirectoryFromDisk(entry.path, entry.basename, entry.dir);
- },
- .File => {
- try this.appendContentFromDisk(entry.path, try entry.dir.openFile(entry.basename, .{ .read = true }));
- },
- else => {},
- }
- }
- }
- };
-
- pub fn extract(this: *Library, dest: std.fs.Dir, comptime verbose: bool) !void {
- for (this.archive.files) |file| {
- var name_slice = this.archive.metadata[file.name.off..][0..file.name.len :0];
-
- var out = dest.createFileZ(name_slice, .{ .truncate = true }) catch brk: {
- if (std.fs.path.dirname(name_slice)) |dirname| {
- dest.makePath(dirname) catch |err2| {
- std.log.err("error: {s} Failed to mkdir {s}\n", .{ @errorName(err2), dirname });
- continue;
- };
- }
-
- break :brk dest.createFileZ(name_slice, .{ .truncate = true }) catch |err2| {
- std.log.err("error: {s} Failed to create file: {s}\n", .{ @errorName(err2), name_slice });
- continue;
- };
- };
-
- if (file.data.len > std.mem.page_size) {
- if (comptime Environment.isLinux) {
- _ = std.os.system.fallocate(out.handle, 0, 0, @intCast(i64, file.data.len));
- } else if (comptime Environment.isMac) {
- try C.preallocate_file(
- out.handle,
- @intCast(std.os.off_t, 0),
- @intCast(std.os.off_t, file.data.len),
- );
- }
- }
-
- var remain: usize = file.data.len;
- var written: usize = 0;
- var in_off: usize = file.data.off;
-
- while (remain > 0) {
- const wrote = try std.os.copy_file_range(
- this.fd.?,
- in_off,
- out.handle,
- written,
- remain,
- 0,
- );
- in_off += wrote;
- remain -= wrote;
- written += wrote;
- }
-
- if (verbose) {
- std.log.info("Extracted file: {s} ({d} bytes)\n", .{ name_slice, written });
- }
- }
- }
-
- pub fn load(
- fd: std.os.fd_t,
- allocator: *std.mem.Allocator,
- ) !Library {
- var file = std.fs.File{ .handle = fd };
-
- var header_buf: Header = std.mem.zeroes(Header);
- var header = file.pread(&header_buf, 0) catch |err| {
- std.log.err("Archive is corrupt. Failed to read header: {s}", .{@errorName(err)});
- return err;
- };
-
- const content_offset = std.mem.readIntNative(u32, header_buf[magic_bytes.len..][0..4]);
-
- const end = file.getEndPos() catch |err| {
- std.log.err("Unable to get archive end position {s}", .{@errorName(err)});
- return error.IOError;
- };
-
- if (content_offset == 0 or std.math.maxInt(u32) == content_offset) {
- std.log.err("Archive is corrupt. content_offset {d} is invalid", .{content_offset});
- return error.CorruptArchive;
- }
-
- if (content_offset >= end) {
- std.log.err("Archive is corrupt. content_offset is {d} greater than end of file", .{content_offset});
- return error.CorruptArchive;
- }
-
- var metadata_buf = try allocator.alloc(u8, end - content_offset);
- var metadata = file.preadAll(metadata_buf, content_offset) catch |err| {
- std.log.err("Error reading archive metadata {s}", .{@errorName(err)});
- return err;
- };
- var reader = Schema.Reader.init(metadata_buf, allocator);
- var archive = Hop.Archive.decode(&reader) catch |err| {
- std.log.err("Archive is corrupt. Failed to decode archive: {s}", .{@errorName(err)});
- return err;
- };
-
- return Library{ .fd = fd, .archive = archive, .allocator = allocator, .metadata_bytes = metadata_buf };
- }
-};
diff --git a/src/hop/schema.zig b/src/hop/schema.zig
deleted file mode 100644
index 8e8a15eaf..000000000
--- a/src/hop/schema.zig
+++ /dev/null
@@ -1,511 +0,0 @@
-const std = @import("std");
-
-pub const Reader = struct {
- const Self = @This();
- pub const ReadError = error{EOF};
-
- buf: []u8,
- remain: []u8,
- allocator: *std.mem.Allocator,
-
- pub fn init(buf: []u8, allocator: *std.mem.Allocator) Reader {
- return Reader{
- .buf = buf,
- .remain = buf,
- .allocator = allocator,
- };
- }
-
- pub fn read(this: *Self, count: usize) ![]u8 {
- const read_count = @minimum(count, this.remain.len);
- if (read_count < count) {
- return error.EOF;
- }
-
- var slice = this.remain[0..read_count];
-
- this.remain = this.remain[read_count..];
-
- return slice;
- }
-
- pub inline fn readAs(this: *Self, comptime T: type) !T {
- if (!std.meta.trait.hasUniqueRepresentation(T)) {
- @compileError(@typeName(T) ++ " must have unique representation.");
- }
-
- return std.mem.bytesAsValue(T, try this.read(@sizeOf(T)));
- }
-
- pub inline fn readByte(this: *Self) !u8 {
- return (try this.read(1))[0];
- }
-
- pub fn readEnum(this: *Self, comptime Enum: type) !Enum {
- const E = error{
- /// An integer was read, but it did not match any of the tags in the supplied enum.
- InvalidValue,
- };
- const type_info = @typeInfo(Enum).Enum;
- const tag = try this.readInt(type_info.tag_type);
-
- inline for (std.meta.fields(Enum)) |field| {
- if (tag == field.value) {
- return @field(Enum, field.name);
- }
- }
-
- return E.InvalidValue;
- }
-
- pub inline fn readArray(this: *Self, comptime T: type) ![]const T {
- const length = try this.readInt(u32);
- if (length == 0) {
- return &([_]T{});
- }
-
- switch (comptime T) {
- u8 => {
- return try this.read(length);
- },
- u16, u32, i8, i16, i32 => {
- var i: u32 = 0;
- var array = try this.allocator.alloc(T, length);
- while (i < length) : (i += 1) {
- array[i] = std.mem.readIntSliceNative(T, (try this.read(@sizeOf(T)))[0..@sizeOf(T)]);
- }
- return array;
- },
- [:0]const u8, []const u8 => {
- var i: u32 = 0;
- var array = try this.allocator.alloc(T, length);
- while (i < length) : (i += 1) {
- array[i] = try this.readArray(u8);
- }
- return array;
- },
- else => {
- switch (comptime @typeInfo(T)) {
- .Struct => |Struct| {
- switch (Struct.layout) {
- .Packed => {
- const sizeof = @sizeOf(T);
- var slice = try this.read(sizeof * length);
- return std.mem.bytesAsSlice(T, slice);
- },
- else => {},
- }
- },
- .Enum => |type_info| {
- const enum_values = try this.read(length * @sizeOf(type_info.tag_type));
- return @ptrCast([*]T, enum_values.ptr)[0..length];
- },
- else => {},
- }
-
- var i: u32 = 0;
- var array = try this.allocator.alloc(T, length);
- while (i < length) : (i += 1) {
- array[i] = try this.readValue(T);
- }
-
- return array;
- },
- }
- }
-
- pub inline fn readByteArray(this: *Self) ![]u8 {
- const length = try this.readInt(u32);
- if (length == 0) {
- return &([_]u8{});
- }
-
- return try this.read(@as(usize, length));
- }
-
- pub inline fn readInt(this: *Self, comptime T: type) !T {
- var slice = try this.read(@sizeOf(T));
-
- return std.mem.readIntSliceNative(T, slice);
- }
-
- pub inline fn readBool(this: *Self) !bool {
- return (try this.readByte()) > 0;
- }
-
- pub inline fn readValue(this: *Self, comptime T: type) !T {
- switch (comptime T) {
- bool => {
- return try this.readBool();
- },
- u8 => {
- return try this.readByte();
- },
- [*:0]const u8, [:0]const u8, []const u8 => {
- return try this.readArray(u8);
- },
-
- []const [:0]const u8, []const [*:0]const u8, []const []const u8 => {
- return try this.readArray([]const u8);
- },
- []u8, [:0]u8, [*:0]u8 => {
- return try this.readArray([]u8);
- },
- u16, u32, i8, i16, i32 => {
- return std.mem.readIntSliceNative(T, try this.read(@sizeOf(T)));
- },
- else => {
- switch (comptime @typeInfo(T)) {
- .Struct => |Struct| {
- switch (Struct.layout) {
- .Packed => {
- const sizeof = @sizeOf(T);
- var slice = try this.read(sizeof);
- return @ptrCast(*T, slice[0..sizeof]).*;
- },
- else => {},
- }
- },
- .Enum => |type_info| {
- return try this.readEnum(T);
- },
- else => {},
- }
-
- return try T.decode(this);
- },
- }
-
- @compileError("Invalid type passed to readValue");
- }
-};
-
-pub fn Writer(comptime WritableStream: type) type {
- return struct {
- const Self = @This();
- writable: WritableStream,
-
- pub fn init(writable: WritableStream) Self {
- return Self{ .writable = writable };
- }
-
- pub inline fn write(this: *Self, bytes: anytype) !void {
- _ = try this.writable.write(bytes);
- }
-
- pub inline fn writeByte(this: *Self, byte: u8) !void {
- _ = try this.writable.write(&[1]u8{byte});
- }
-
- pub inline fn writeInt(this: *Self, int: anytype) !void {
- try this.write(std.mem.asBytes(&int));
- }
-
- pub inline fn writeFieldID(this: *Self, comptime id: comptime_int) !void {
- try this.writeByte(id);
- }
-
- pub inline fn writeEnum(this: *Self, val: anytype) !void {
- try this.writeInt(@enumToInt(val));
- }
-
- pub fn writeValue(this: *Self, comptime SliceType: type, slice: SliceType) !void {
- switch (SliceType) {
- []u16,
- []u32,
- []i16,
- []i32,
- []i8,
- []const u16,
- []const u32,
- []const i16,
- []const i32,
- []const i8,
- [:0]u16,
- [:0]u32,
- [:0]i16,
- [:0]i32,
- [:0]i8,
- [:0]const u16,
- [:0]const u32,
- [:0]const i16,
- [:0]const i32,
- [:0]const i8,
- [*:0]u16,
- [*:0]u32,
- [*:0]i16,
- [*:0]i32,
- [*:0]i8,
- [*:0]const u16,
- [*:0]const u32,
- [*:0]const i16,
- [*:0]const i32,
- [*:0]const i8,
- => {
- try this.writeArray(SliceType, slice);
- },
-
- []u8,
- []const u8,
- [:0]u8,
- [:0]const u8,
- [*:0]u8,
- [*:0]const u8,
- => {
- try this.writeArray(u8, slice);
- },
-
- u8 => {
- try this.write(slice);
- },
- u16, u32, i16, i32, i8 => {
- try this.write(std.mem.asBytes(slice));
- },
-
- else => {
- try slice.encode(this);
- },
- }
- }
-
- pub inline fn writeArray(this: *Self, comptime T: type, slice: anytype) !void {
- try this.writeInt(@truncate(u32, slice.len));
-
- switch (T) {
- u8 => {
- try this.write(slice);
- },
- u16, u32, i16, i32, i8 => {
- try this.write(std.mem.sliceAsBytes(slice));
- },
- [:0]u8,
- []u8,
- []u16,
- []u32,
- []i16,
- []i32,
- []i8,
- []const u8,
- [:0]const u8,
- []const u16,
- []const u32,
- []const i16,
- []const i32,
- []const i8,
- [:0]u16,
- [:0]u32,
- [:0]i16,
- [:0]i32,
- [:0]i8,
- [:0]const u16,
- [:0]const u32,
- [:0]const i16,
- [:0]const i32,
- [:0]const i8,
- [*:0]u16,
- [*:0]u32,
- [*:0]i16,
- [*:0]i32,
- [*:0]i8,
- [*:0]const u16,
- [*:0]const u32,
- [*:0]const i16,
- [*:0]const i32,
- [*:0]const i8,
- => {
- for (slice) |num_slice| {
- try this.writeArray(std.meta.Child(@TypeOf(num_slice)), num_slice);
- }
- },
- else => {
- for (slice) |val| {
- try val.encode(this);
- }
- },
- }
- }
-
- pub inline fn endMessage(this: *Self) !void {
- try this.writeByte(0);
- }
- };
-}
-
-pub const ByteWriter = Writer(*std.io.FixedBufferStream([]u8));
-pub const FileWriter = Writer(std.fs.File);
-
-pub const Hop = struct {
- pub const StringPointer = packed struct {
- /// off
- off: u32 = 0,
-
- /// len
- len: u32 = 0,
-
- pub fn decode(reader: anytype) anyerror!StringPointer {
- var this = std.mem.zeroes(StringPointer);
-
- this.off = try reader.readValue(u32);
- this.len = try reader.readValue(u32);
- return this;
- }
-
- pub fn encode(this: *const @This(), writer: anytype) anyerror!void {
- try writer.writeInt(this.off);
- try writer.writeInt(this.len);
- }
- };
-
- pub const File = packed struct {
- /// name
- name: StringPointer,
-
- /// name_hash
- name_hash: u32 = 0,
-
- /// chmod
- chmod: u32 = 0,
-
- /// mtime
- mtime: u32 = 0,
-
- /// ctime
- ctime: u32 = 0,
-
- /// data
- data: StringPointer,
-
- pub fn decode(reader: anytype) anyerror!File {
- var this = File{ .name = StringPointer{}, .data = .{} };
-
- this.name = try reader.readValue(StringPointer);
- this.name_hash = try reader.readValue(u32);
- this.chmod = try reader.readValue(u32);
- this.mtime = try reader.readValue(u32);
- this.ctime = try reader.readValue(u32);
- this.data = try reader.readValue(StringPointer);
- return this;
- }
-
- pub fn encode(this: *const @This(), writer: anytype) anyerror!void {
- try writer.writeValue(@TypeOf(this.name), this.name);
- try writer.writeInt(this.name_hash);
- try writer.writeInt(this.chmod);
- try writer.writeInt(this.mtime);
- try writer.writeInt(this.ctime);
- try writer.writeValue(@TypeOf(this.data), this.data);
- }
- };
-
- pub const Directory = packed struct {
- /// name
- name: StringPointer,
-
- /// name_hash
- name_hash: u32 = 0,
-
- /// chmod
- chmod: u32 = 0,
-
- pub fn decode(reader: anytype) anyerror!Directory {
- var this = Directory{
- .name = StringPointer{},
- };
-
- this.name = try reader.readValue(StringPointer);
- this.name_hash = try reader.readValue(u32);
- this.chmod = try reader.readValue(u32);
-
- return this;
- }
-
- pub fn encode(this: *const @This(), writer: anytype) anyerror!void {
- try writer.writeValue(@TypeOf(this.name), this.name);
- try writer.writeInt(this.name_hash);
- try writer.writeInt(this.chmod);
- }
- };
-
- pub const Archive = struct {
- /// version
- version: ?u32 = null,
-
- /// content_offset
- content_offset: ?u32 = null,
-
- /// files
- files: []align(1) const File,
-
- /// files
- directories: []align(1) const Directory,
-
- /// name_hashes
- name_hashes: []align(1) const u32,
-
- /// metadata
- metadata: []align(1) const u8,
-
- pub fn decode(reader: anytype) anyerror!Archive {
- var this = std.mem.zeroes(Archive);
-
- while (true) {
- switch (try reader.readByte()) {
- 0 => {
- return this;
- },
-
- 1 => {
- this.version = try reader.readValue(u32);
- },
- 2 => {
- this.content_offset = try reader.readValue(u32);
- },
- 3 => {
- this.files = try reader.readArray(File);
- },
- 4 => {
- this.directories = try reader.readArray(Directory);
- },
- 5 => {
- this.name_hashes = try reader.readArray(u32);
- },
- 6 => {
- this.metadata = try reader.readArray(u8);
- },
- else => {
- return error.InvalidMessage;
- },
- }
- }
- unreachable;
- }
-
- pub fn encode(this: *const @This(), writer: anytype) anyerror!void {
- if (this.version) |version| {
- try writer.writeFieldID(1);
- try writer.writeInt(version);
- }
- if (this.content_offset) |content_offset| {
- try writer.writeFieldID(2);
- try writer.writeInt(content_offset);
- }
- if (this.files.len > 0) {
- try writer.writeFieldID(3);
- try writer.writeArray(File, this.files);
- }
- if (this.directories.len > 0) {
- try writer.writeFieldID(4);
- try writer.writeArray(Directory, this.directories);
- }
- if (this.name_hashes.len > 0) {
- try writer.writeFieldID(5);
- try writer.writeArray(u32, this.name_hashes);
- }
- if (this.metadata.len > 0) {
- try writer.writeFieldID(6);
- try writer.writeArray(u8, this.metadata);
- }
- try writer.endMessage();
- }
- };
-};
diff --git a/src/install/install.zig b/src/install/install.zig
index 486613023..ab3bddb59 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -3138,15 +3138,13 @@ const PackageInstall = struct {
// Slower than clonefile
clonefile_each_dir,
- // Slow!
+ // On macOS, slow.
+ // On Linux, fast.
hardlink,
// Slowest if single-threaded
copyfile,
- copy_file_range,
- io_uring,
-
const BackendSupport = std.EnumArray(Method, bool);
pub const macOS = BackendSupport.initDefault(false, .{
@@ -3159,7 +3157,7 @@ const PackageInstall = struct {
pub const linux = BackendSupport.initDefault(false, .{
.io_uring = true,
.hardlink = true,
- .copy_file_range = true,
+ .copyfile = true,
});
pub inline fn isSupported(this: Method) bool {
@@ -3168,13 +3166,6 @@ const PackageInstall = struct {
return false;
}
-
- pub inline fn isSync(this: Method) bool {
- return switch (this) {
- .hardlink, .clonefile_each_dir, .clonefile => true,
- else => false,
- };
- }
};
pub fn verify(
@@ -3538,9 +3529,9 @@ const PackageInstall = struct {
// we'll catch it the next error
if (!skip_delete) this.destination_dir.deleteTree(std.mem.span(this.destination_dir_subpath)) catch {};
- if (comptime Environment.isMac) {
- switch (supported_method) {
- .clonefile => {
+ switch (supported_method) {
+ .clonefile => {
+ if (comptime Environment.isMac) {
// First, attempt to use clonefile
// if that fails due to ENOTSUP, mark it as unsupported and then fall back to copyfile
if (this.installWithClonefile()) |result| {
@@ -3558,8 +3549,10 @@ const PackageInstall = struct {
},
}
}
- },
- .clonefile_each_dir => {
+ }
+ },
+ .clonefile_each_dir => {
+ if (comptime Environment.isMac) {
if (this.installWithClonefileEachDir()) |result| {
return result;
} else |err| {
@@ -3575,36 +3568,34 @@ const PackageInstall = struct {
},
}
}
- },
- .hardlink => {
- if (this.installWithHardlink()) |result| {
- return result;
- } else |err| {
- switch (err) {
- error.NotSupported => {
- supported_method = .copyfile;
- },
- error.FileNotFound => return Result{
- .fail = .{ .err = error.FileNotFound, .step = .opening_cache_dir },
- },
- else => return Result{
- .fail = .{ .err = err, .step = .copying_files },
- },
- }
+ }
+ },
+ .hardlink => {
+ if (this.installWithHardlink()) |result| {
+ return result;
+ } else |err| {
+ switch (err) {
+ error.NotSupported => {
+ supported_method = .copyfile;
+ },
+ error.FileNotFound => return Result{
+ .fail = .{ .err = error.FileNotFound, .step = .opening_cache_dir },
+ },
+ else => return Result{
+ .fail = .{ .err = err, .step = .copying_files },
+ },
}
- },
- else => {},
- }
-
- if (supported_method != .copyfile) return Result{
- .success = void{},
- };
-
- return this.installWithCopyfile();
+ }
+ },
+ else => {},
}
- // TODO: linux io_uring
+ if (supported_method != .copyfile) return Result{
+ .success = void{},
+ };
+ // TODO: linux io_uring
+ return this.installWithCopyfile();
}
};
@@ -5922,6 +5913,7 @@ pub const PackageManager = struct {
node_modules_folder: std.fs.Dir,
};
+ /// Install versions of a package which are waiting on a network request
pub fn installEnqueuedPackages(
this: *PackageInstaller,
package_id: PackageID,
@@ -6197,125 +6189,64 @@ pub const PackageManager = struct {
.successfully_installed = try std.DynamicBitSetUnmanaged.initEmpty(lockfile.packages.len, this.allocator),
};
- // When it's a Good Idea, run the install in single-threaded
- // From benchmarking, apfs clonefile() is ~6x faster than copyfile() on macOS
- // Running it in parallel is the same or slower.
- // However, copyfile() is about 30% faster if run in paralell
- // On Linux, the story here will be similar but with io_uring.
- // We will have to support versions of Linux that do not have io_uring support
- // so in that case, we will still need to support copy_file_range()
- // git installs will always need to run in paralell, and tarball installs probably should too
- run_install: {
- if (PackageInstall.supported_method.isSync()) {
- sync_install: {
- const cwd = std.fs.cwd();
- while (iterator.nextNodeModulesFolder()) |node_modules| {
- try cwd.makePath(std.mem.span(node_modules.relative_path));
- // We deliberately do not close this folder.
- // If the package hasn't been downloaded, we will need to install it later
- // We use this file descriptor to know where to put it.
- var folder = try cwd.openDirZ(node_modules.relative_path, .{
- .iterate = true,
- });
-
- installer.node_modules_folder = folder;
-
- for (node_modules.packages) |package_id| {
- installer.installPackage(@truncate(PackageID, package_id), log_level);
- if (!PackageInstall.supported_method.isSync()) break :sync_install;
- if (this.pending_tasks > 16) {
- try this.runTasks(
- *PackageInstaller,
- &installer,
- PackageInstaller.installEnqueuedPackages,
- log_level,
- );
- }
- }
+ const cwd = std.fs.cwd();
+ while (iterator.nextNodeModulesFolder()) |node_modules| {
+ try cwd.makePath(std.mem.span(node_modules.relative_path));
+ // We deliberately do not close this folder.
+ // If the package hasn't been downloaded, we will need to install it later
+ // We use this file descriptor to know where to put it.
+ var folder = try cwd.openDirZ(node_modules.relative_path, .{
+ .iterate = true,
+ });
- try this.runTasks(
- *PackageInstaller,
- &installer,
- PackageInstaller.installEnqueuedPackages,
- log_level,
- );
- }
+ installer.node_modules_folder = folder;
- while (this.pending_tasks > 0) {
- try this.runTasks(
- *PackageInstaller,
- &installer,
- PackageInstaller.installEnqueuedPackages,
- log_level,
- );
- }
- break :run_install;
+ var remaining = node_modules.packages;
+
+ // cache line is 64 bytes on ARM64 and x64
+ // PackageIDs are 4 bytes
+ // Hence, we can fit up to 64 / 4 = 16 package IDs in a cache line
+ const unroll_count = comptime 64 / @sizeOf(PackageID);
+
+ while (remaining.len > unroll_count) {
+ comptime var i: usize = 0;
+ inline while (i < unroll_count) : (i += 1) {
+ installer.installPackage(remaining[i], comptime log_level);
+ }
+ remaining = remaining[unroll_count..];
+
+ // We want to minimize how often we call this function
+ // That's part of why we unroll this loop
+ if (this.pending_tasks > 0) {
+ try this.runTasks(
+ *PackageInstaller,
+ &installer,
+ PackageInstaller.installEnqueuedPackages,
+ log_level,
+ );
}
}
- // var install_context = try lockfile.allocator.create(PackageInstall.Context);
- // install_context.* = .{
- // .cache_dir = cache_dir,
- // .progress = progress,
- // .metas = metas,
- // .names = names,
- // .resolutions = resolutions,
- // .string_buf = lockfile.buffers.string_bytes.items,
- // .allocator = lockfile.allocator,
- // };
- // install_context.channel = PackageInstall.Task.Channel.init();
- // var ran: usize = summary.skipped + summary.success + summary.fail;
-
- // var tasks = try lockfile.allocator.alloc(PackageInstall.Task, toplevel_count - ran);
- // var task_i: usize = 0;
- // var batch = ThreadPool.Batch{};
- // var remaining_count = task_i;
- // while (toplevel_node_modules.next()) |package_id| {
- // const meta = &metas[package_id];
- // if (meta.isDisabled()) {
- // if (comptime log_level.showProgress()) {
- // install_node.completeOne();
- // install_node.setEstimatedTotalItems(installer.install_count);
- // }
- // continue;
- // }
-
- // tasks[task_i] = PackageInstall.Task{
- // .package_id = @truncate(PackageID, package_id),
- // .destination_dir = node_modules_folder,
- // .ctx = install_context,
- // };
- // batch.push(ThreadPool.Batch.from(&tasks[task_i].task));
- // task_i += 1;
- // }
+ for (remaining) |package_id| {
+ installer.installPackage(@truncate(PackageID, package_id), log_level);
+ }
- // this.thread_pool.schedule(batch);
-
- // while (remaining_count > 0) {
- // while (install_context.channel.tryReadItem() catch null) |item_| {
- // var install_task: *PackageInstall.Task = item_;
- // defer remaining_count -= 1;
- // switch (install_task.result) {
- // .pending => unreachable,
- // .skip => summary.skipped += 1,
- // .success => summary.success += 1,
- // .fail => |cause| {
- // Output.prettyErrorln(
- // "<r><red>error<r>: <b><red>{s}<r> installing <b>{s}<r>",
- // .{ @errorName(cause.err), install_task.ctx.names[install_task.package_id] },
- // );
- // summary.fail += 1;
- // },
- // }
- // }
- // if (comptime log_level.showProgress()) {
- // install_node.completeOne();
- // install_node.setEstimatedTotalItems(installer.install_count);
- // }
-
- // std.atomic.spinLoopHint();
+ try this.runTasks(
+ *PackageInstaller,
+ &installer,
+ PackageInstaller.installEnqueuedPackages,
+ log_level,
+ );
+ }
+
+ while (this.pending_tasks > 0) {
+ try this.runTasks(
+ *PackageInstaller,
+ &installer,
+ PackageInstaller.installEnqueuedPackages,
+ log_level,
+ );
}
- // }
summary.successfully_installed = installer.successfully_installed;
outer: for (installer.platform_binlinks.items) |deferred| {
diff --git a/src/libarchive/libarchive.zig b/src/libarchive/libarchive.zig
index 47ac23645..4e1751e80 100644
--- a/src/libarchive/libarchive.zig
+++ b/src/libarchive/libarchive.zig
@@ -284,8 +284,8 @@ pub const BufferReadStream = struct {
_ = lib.archive_read_support_format_tar(this.archive);
_ = lib.archive_read_support_format_gnutar(this.archive);
- _ = lib.archive_read_support_filter_gzip(this.archive);
- _ = lib.archive_read_support_filter_none(this.archive);
+ _ = lib.archive_read_support_compression_gzip(this.archive);
+ // _ = lib.archive_read_support_filter_none(this.archive);
const rc = lib.archive_read_open_memory(this.archive, this.buf.ptr, this.buf.len);
@@ -523,176 +523,6 @@ pub const Archive = struct {
}
}
- const SeekableBufferedWriter = struct {
- pos: u64 = 0,
- buf: [std.mem.page_size]u8 = undefined,
- len: usize = 0,
- fd: std.os.fd_t = 0,
-
- pub fn flush(this: *SeekableBufferedWriter) !usize {
- const end = this.len + this.pos;
- var off: usize = this.pos;
- const initial = this.pos;
- defer this.pos = off;
- var slice = this.buf[0..this.len];
- while (slice.len > 0) {
- const written = try std.os.pwrite(this.fd, slice, off);
- slice = slice[written..];
- off += written;
- }
- this.len = 0;
- return off - initial;
- }
-
- pub fn write(this: *SeekableBufferedWriter, buf: []const u8) !usize {
- if (this.buf.len - this.len < 32) {
- _ = try this.flush();
- }
- var queue = buf;
- while (queue.len > 0) {
- var to_write = @minimum(this.buf.len - this.len, queue.len);
- if (to_write == 0 and this.len > 0) {
- _ = try this.flush();
- to_write = @minimum(this.buf.len - this.len, queue.len);
- }
-
- var remainder = queue[0..to_write];
- queue = queue[remainder.len..];
- @memcpy(this.buf[this.len..].ptr, remainder.ptr, remainder.len);
- this.len += remainder.len;
- }
-
- return buf.len;
- }
- };
-
- pub fn convertToHop(
- hop: *Hop.Builder,
- file_buffer: []const u8,
- ctx: ?*Archive.Context,
- comptime FilePathAppender: type,
- appender: FilePathAppender,
- comptime depth_to_skip: usize,
- comptime log: bool,
- ) !u32 {
- var entry: *lib.archive_entry = undefined;
- var ext: *lib.archive = undefined;
-
- const flags = @enumToInt(Flags.Extract.time) | @enumToInt(Flags.Extract.perm) | @enumToInt(Flags.Extract.acl) | @enumToInt(Flags.Extract.fflags);
- var stream: BufferReadStream = undefined;
- stream.init(file_buffer);
- defer stream.deinit();
- _ = stream.openRead();
- var archive = stream.archive;
- var count: u32 = 0;
-
- var chunk_remain: usize = 0;
- var chunk_offset: isize = 0;
- var chunk_output_offset: isize = 0;
- var chunk_size: usize = 0;
- var chunk_buf: ?[*]u8 = null;
- const handle = hop.destination.handle;
-
- var writer = SeekableBufferedWriter{
- .pos = try hop.destination.getPos(),
- .fd = hop.destination.handle,
- };
-
- loop: while (true) {
- const r = @intToEnum(Status, lib.archive_read_next_header(archive, &entry));
-
- switch (r) {
- Status.eof => break :loop,
- Status.failed, Status.fatal, Status.retry => return error.Fail,
- else => {
- var pathname: [:0]const u8 = std.mem.sliceTo(lib.archive_entry_pathname(entry).?, 0);
- var tokenizer = std.mem.tokenize(u8, std.mem.span(pathname), std.fs.path.sep_str);
- comptime var depth_i: usize = 0;
-
- inline while (depth_i < depth_to_skip) : (depth_i += 1) {
- if (tokenizer.next() == null) continue :loop;
- }
-
- const Kind = std.fs.Dir.Entry.Kind;
- const entry_kind: Kind = switch (lib.archive_entry_filetype(entry)) {
- std.os.S_IFDIR => Kind.Directory,
- std.os.S_IFREG => Kind.File,
- else => continue :loop,
- };
-
- var pathname_ = tokenizer.rest();
-
- count += 1;
- const mode = @truncate(u32, lib.archive_entry_perm(entry));
- const slice = std.mem.span(pathname);
- const name = hop.appendMetadata(pathname_) catch unreachable;
-
- if (entry_kind == .Directory) {
- hop.directories.append(hop.allocator, .{
- .name = name,
- .name_hash = @truncate(u32, std.hash.Wyhash.hash(0, pathname_)),
- .chmod = mode,
- }) catch unreachable;
-
- if (comptime log) {
- Output.prettyErrorln("Dir: {s}", .{
- pathname_,
- });
- }
- } else {
- var data = Hop.StringPointer{
- .off = @truncate(u32, writer.pos),
- };
-
- chunk_offset = 0;
- chunk_output_offset = 0;
- const size = lib.archive_entry_size(entry);
-
- var data_block_status: c_int = lib.archive_read_data_block(archive, @ptrCast([*c]*const c_void, &chunk_buf), &chunk_size, &chunk_offset);
-
- while (data_block_status == lib.ARCHIVE_OK) : (data_block_status = lib.archive_read_data_block(archive, @ptrCast([*c]*const c_void, &chunk_buf), &chunk_size, &chunk_offset)) {
- if (chunk_offset > chunk_output_offset) {
- writer.pos = @intCast(usize, @intCast(isize, writer.pos) + chunk_offset - chunk_output_offset);
- }
-
- while (chunk_size > 0) {
- const remain = size - chunk_output_offset;
- chunk_size = @minimum(@intCast(usize, remain), chunk_size);
- // const written = try std.os.pwrite(handle, chunk_buf.?[0..chunk_size], writer.pos);
- // writer.pos += written;
- const written = try writer.write(chunk_buf.?[0..chunk_size]);
- chunk_buf.? += written;
- chunk_size -= written;
- chunk_output_offset += @intCast(i64, written);
- }
- }
-
- data.len = @intCast(u32, size);
- _ = try writer.flush();
-
- if (comptime log) {
- Output.prettyErrorln("File: {s} - [{d}, {d}]", .{ pathname_, data.off, data.len });
- }
-
- hop.files.append(
- hop.allocator,
- Hop.File{
- .name = name,
- .name_hash = @truncate(u32, std.hash.Wyhash.hash(0, pathname_)),
- .chmod = mode,
- .data = data,
- },
- ) catch unreachable;
- }
- },
- }
- }
-
- try hop.destination.seekTo(writer.pos);
-
- return count;
- }
-
pub fn extractToDisk(
file_buffer: []const u8,
root: []const u8,