sleep

Owner: IIIlllIIIllI URL: git@github.com:nyangkosense/sleep.git

sleep.zig

// sleep.zig

const syscall = struct {
    pub const write = 1;
    pub const nanosleep = 35;
    pub const exit = 60;
};

const EINTR = 4;
const MAX_SECONDS = 2147483647; // INT_MAX

fn writeSyscall(fd: u32, buf: [*]const u8, count: usize) usize {
    var result: usize = undefined;
    asm volatile (
        \\ mov %[syscall_num], %%rax
        \\ mov %[fd], %%rdi
        \\ mov %[buf], %%rsi
        \\ mov %[count], %%rdx
        \\ syscall
        : [result] "={rax}" (result)
        : [syscall_num] "r" (@as(usize, syscall.write)),
          [fd] "r" (@as(usize, fd)),
          [buf] "r" (@intFromPtr(buf)),
          [count] "r" (count)
        : "rax", "rdi", "rsi", "rdx", "rcx", "r11", "memory"
    );
    return result;
}

fn nanosleepSyscall(req: *const Timespec, rem: ?*Timespec) isize {
    var result: isize = undefined;
    asm volatile (
        \\ mov %[syscall_num], %%rax
        \\ mov %[req], %%rdi
        \\ mov %[rem], %%rsi
        \\ syscall
        : [result] "={rax}" (result)
        : [syscall_num] "r" (@as(usize, syscall.nanosleep)),
          [req] "r" (@intFromPtr(req)),
          [rem] "r" (if (rem) |r| @intFromPtr(r) else @as(usize, 0))
        : "rax", "rdi", "rsi", "rcx", "r11", "memory"
    );
    return result;
}

fn exitSyscall(code: u8) noreturn {
    asm volatile (
        \\ mov %[syscall_num], %%rax
        \\ mov %[code], %%rdi
        \\ syscall
        :
        : [syscall_num] "r" (@as(usize, syscall.exit)),
          [code] "r" (@as(usize, code))
        : "rax", "rdi", "memory"
    );
    unreachable;
}

const Timespec = extern struct {
    tv_sec: u64,
    tv_nsec: u64,
};

const ParseResult = struct {
    seconds: u64,
    nanos: u64,
};

fn parseTime(str: [*:0]const u8) ?ParseResult {
    var seconds: u64 = 0;
    var nanos: u64 = 0;
    var i: usize = 0;
    var saw_dot = false;
    var decimal_places: u32 = 0;
    
    if (str[0] == 0) return null;
    
    while (str[i] != 0) : (i += 1) {
        const c = str[i];
        
        if (c == '.') {
            if (saw_dot) return null; // Multiple dots
            saw_dot = true;
            continue;
        }
        
        if (c < '0' or c > '9') return null;
        
        const digit = c - '0';
        
        if (!saw_dot) {
            const new_seconds = seconds * 10 + digit;
            if (new_seconds < seconds or new_seconds > MAX_SECONDS) return null;
            seconds = new_seconds;
        } else {
            if (decimal_places >= 9) continue;
            nanos = nanos * 10 + digit;
            decimal_places += 1;
        }
    }
    
    while (decimal_places < 9) : (decimal_places += 1) {
        nanos *= 10;
    }
    
    return ParseResult{ .seconds = seconds, .nanos = nanos };
}

fn writeNumber(n: u64) void {
    if (n == 0) {
        _ = writeSyscall(1, "0", 1);
        return;
    }
    
    var buf: [20]u8 = undefined; // u64 max is 20 digits
    var i: usize = 0;
    var num = n;
    
    while (num > 0) : (i += 1) {
        buf[i] = @as(u8, @intCast(num % 10)) + '0';
        num /= 10;
    }

    var j: usize = 0;
    while (j < i) : (j += 1) {
        const c = buf[i - 1 - j];
        _ = writeSyscall(1, &c, 1);
    }
}

fn start(stack_ptr: [*]usize) noreturn {
    const argc = stack_ptr[0];
    const argv: [*][*:0]u8 = @ptrCast(stack_ptr + 1);
    
    if (argc != 2) {
        const usage_msg = "Usage: sleep NUMBER[.FRACTION]\n";
        _ = writeSyscall(2, usage_msg, usage_msg.len);
        exitSyscall(1);
    }
    
    const parsed = parseTime(argv[1]) orelse {
        const error_msg = "sleep: invalid time interval\n";
        _ = writeSyscall(2, error_msg, error_msg.len);
        exitSyscall(1);
    };
    
    var req = Timespec{
        .tv_sec = parsed.seconds,
        .tv_nsec = parsed.nanos,
    };
    var rem = Timespec{
        .tv_sec = 0,
        .tv_nsec = 0,
    };
    
    while (true) {
        const result = nanosleepSyscall(&req, &rem);
        
        if (result == 0) break;
        
        const err = @as(usize, @bitCast(-result));
        if (err != EINTR) break;
        
        req = rem;
    }
    
    exitSyscall(0);
}

pub export fn _start() callconv(.Naked) noreturn {
    @setRuntimeSafety(false);
    asm volatile (
        \\ xor %edi, %edi
        \\ mov %rsp, %rsi
        \\ jmp *%[start_fn]
        :
        : [start_fn] "r" (&start)
        : "rdi", "rsi", "memory"
    );
    unreachable;
}