aboutsummaryrefslogtreecommitdiff
path: root/src/node_module_bundle.zig
blob: 11150f376404b538f24550430dca696023f60c26 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
const Api = @import("./api/schema.zig").Api;
const std = @import("std");
usingnamespace @import("global.zig");

pub const NodeModuleBundle = struct {
    container: *Api.JavascriptBundleContainer,
    bundle: *Api.JavascriptBundle,
    allocator: *std.mem.Allocator,
    fd: FileDescriptorType = 0,

    pub const magic_bytes = "#!/usr/bin/env speedy\n\n";
    threadlocal var jsbundle_prefix: [magic_bytes.len + 5]u8 = undefined;

    pub fn getCodeEndPosition(stream: anytype, comptime needs_seek: bool) !u32 {
        if (needs_seek) try stream.seekTo(0);

        const read_bytes = try stream.read(&jsbundle_prefix);
        if (read_bytes != jsbundle_prefix.len) {
            return error.JSBundleBadHeaderTooShort;
        }

        return std.mem.readIntNative(u32, jsbundle_prefix[magic_bytes.len .. magic_bytes.len + 4]);
    }

    pub fn loadBundle(allocator: *std.mem.Allocator, stream: anytype) !NodeModuleBundle {
        const end = try getCodeEndPosition(stream);
        try stream.seekTo(end + 1);
        var reader = stream.reader();
        var container = try Api.JavascriptBundleContainer.decode(allocator, reader);
        return NodeModuleBundle{
            .allocator = allocator,
            .container = container,
            .bundle = container.bundle,
            .fd = if (std.meta.trait.hasField("handle")(stream)) stream.handle else 0,
        };
    }

    pub fn printBundle(
        comptime StreamType: type,
        input: StreamType,
        comptime DestinationStreamType: type,
        output: DestinationStreamType,
    ) !void {
        const BufferStreamContext = struct {
            pub fn run(in: StreamType, out: DestinationStreamType, end_at: u32) !void {
                var buf: [4096]u8 = undefined;
                var remain = @intCast(i64, end_at);
                var read_amount: i64 = @intCast(i64, in.read(&buf) catch 0);
                while (remain > 0 and read_amount > 0) {
                    remain -= @intCast(i64, try out.write(buf[0..@intCast(usize, std.math.min(read_amount, remain))]));
                    read_amount = @intCast(i64, in.read(&buf) catch 0);
                }

                _ = try out.write(buf[0..@intCast(usize, remain + 1)]);
            }
        };
        if (isMac) {
            // darwin only allows reading ahead on/off, not specific amount
            _ = std.os.fcntl(input.handle, std.os.F_RDAHEAD, 1) catch 0;
        }
        const end = try getCodeEndPosition(input, false);

        try BufferStreamContext.run(
            input,
            output,
            end,
        );
    }
};