zig

fork of https://codeberg.org/ziglang/zig
Log | Tree | Refs | README | LICENSE

blob 5d1e7a78 (9592B) - Raw


      1 import "syscall.zig";
      2 import "errno.zig";
      3 import "math.zig";
      4 
      5 pub const stdin_fileno = 0;
      6 pub const stdout_fileno = 1;
      7 pub const stderr_fileno = 2;
      8 
      9 pub var stdin = InStream {
     10     .fd = stdin_fileno,
     11 };
     12 
     13 pub var stdout = OutStream {
     14     .fd = stdout_fileno,
     15     .buffer = undefined,
     16     .index = 0,
     17 };
     18 
     19 pub var stderr = OutStream {
     20     .fd = stderr_fileno,
     21     .buffer = undefined,
     22     .index = 0,
     23 };
     24 
     25 /// The function received invalid input at runtime. An Invalid error means a
     26 /// bug in the program that called the function.
     27 pub error Invalid;
     28 
     29 /// When an Unexpected error occurs, code that emitted the error likely needs
     30 /// a patch to recognize the unexpected case so that it can handle it and emit
     31 /// a more specific error.
     32 pub error Unexpected;
     33 
     34 pub error DiskQuota;
     35 pub error FileTooBig;
     36 pub error SigInterrupt;
     37 pub error Io;
     38 pub error NoSpaceLeft;
     39 pub error BadPerm;
     40 pub error PipeFail;
     41 pub error BadFd;
     42 
     43 const buffer_size = 4 * 1024;
     44 const max_u64_base10_digits = 20;
     45 const max_f64_digits = 65;
     46 
     47 pub struct OutStream {
     48     fd: isize,
     49     buffer: [buffer_size]u8,
     50     index: isize,
     51 
     52     pub fn print_str(os: &OutStream, str: []const u8) -> %isize {
     53         var src_bytes_left = str.len;
     54         var src_index: @typeof(str.len) = 0;
     55         const dest_space_left = os.buffer.len - os.index;
     56 
     57         while (src_bytes_left > 0) {
     58             const copy_amt = min_isize(dest_space_left, src_bytes_left);
     59             @memcpy(&os.buffer[os.index], &str[src_index], copy_amt);
     60             os.index += copy_amt;
     61             if (os.index == os.buffer.len) {
     62                 %return os.flush();
     63             }
     64             src_bytes_left -= copy_amt;
     65         }
     66         return str.len;
     67     }
     68 
     69     /// Prints a byte buffer, flushes the buffer, then returns the number of
     70     /// bytes printed. The "f" is for "flush".
     71     pub fn printf(os: &OutStream, str: []const u8) -> %isize {
     72         const byte_count = %return os.print_str(str);
     73         %return os.flush();
     74         return byte_count;
     75     }
     76 
     77     pub fn print_u64(os: &OutStream, x: u64) -> %isize {
     78         if (os.index + max_u64_base10_digits >= os.buffer.len) {
     79             %return os.flush();
     80         }
     81         const amt_printed = buf_print_u64(os.buffer[os.index...], x);
     82         os.index += amt_printed;
     83 
     84         return amt_printed;
     85     }
     86 
     87     pub fn print_i64(os: &OutStream, x: i64) -> %isize {
     88         if (os.index + max_u64_base10_digits >= os.buffer.len) {
     89             %return os.flush();
     90         }
     91         const amt_printed = buf_print_i64(os.buffer[os.index...], x);
     92         os.index += amt_printed;
     93 
     94         return amt_printed;
     95     }
     96 
     97     pub fn print_f64(os: &OutStream, x: f64) -> %isize {
     98         if (os.index + max_f64_digits >= os.buffer.len) {
     99             %return os.flush();
    100         }
    101         const amt_printed = buf_print_f64(os.buffer[os.index...], x, 4);
    102         os.index += amt_printed;
    103 
    104         return amt_printed;
    105     }
    106 
    107     pub fn flush(os: &OutStream) -> %void {
    108         const amt_written = write(os.fd, &os.buffer[0], os.index);
    109         os.index = 0;
    110         if (amt_written < 0) {
    111             return switch (-amt_written) {
    112                 EINVAL => unreachable{},
    113                 EDQUOT => error.DiskQuota,
    114                 EFBIG  => error.FileTooBig,
    115                 EINTR  => error.SigInterrupt,
    116                 EIO    => error.Io,
    117                 ENOSPC => error.NoSpaceLeft,
    118                 EPERM  => error.BadPerm,
    119                 EPIPE  => error.PipeFail,
    120                 else   => error.Unexpected,
    121             }
    122         }
    123     }
    124 
    125     pub fn close(os: &OutStream) -> %void {
    126         const closed = close(os.fd);
    127         if (closed < 0) {
    128             return switch (-closed) {
    129                 EIO => error.Io,
    130                 EBADF => error.BadFd,
    131                 EINTR => error.SigInterrupt,
    132                 else => error.Unexpected,
    133             }
    134         }
    135     }
    136 }
    137 
    138 pub struct InStream {
    139     fd: isize,
    140 
    141     pub fn read(is: &InStream, buf: []u8) -> %isize {
    142         const amt_read = read(is.fd, &buf[0], buf.len);
    143         if (amt_read < 0) {
    144             return switch (-amt_read) {
    145                 EINVAL => unreachable{},
    146                 EFAULT => unreachable{},
    147                 EBADF  => error.BadFd,
    148                 EINTR  => error.SigInterrupt,
    149                 EIO    => error.Io,
    150                 else   => error.Unexpected,
    151             }
    152         }
    153         return amt_read;
    154     }
    155 
    156     pub fn close(is: &InStream) -> %void {
    157         const closed = close(is.fd);
    158         if (closed < 0) {
    159             return switch (-closed) {
    160                 EIO => error.Io,
    161                 EBADF => error.BadFd,
    162                 EINTR => error.SigInterrupt,
    163                 else => error.Unexpected,
    164             }
    165         }
    166     }
    167 }
    168 
    169 #attribute("cold")
    170 pub fn abort() -> unreachable {
    171     raise(SIGABRT);
    172     raise(SIGKILL);
    173     while (true) {}
    174 }
    175 
    176 pub error InvalidChar;
    177 pub error Overflow;
    178 
    179 pub fn parse_u64(buf: []u8, radix: u8) -> %u64 {
    180     var x : u64 = 0;
    181 
    182     for (buf) |c| {
    183         const digit = char_to_digit(c);
    184 
    185         if (digit >= radix) {
    186             return error.InvalidChar;
    187         }
    188 
    189         // x *= radix
    190         if (@mul_with_overflow(u64, x, radix, &x)) {
    191             return error.Overflow;
    192         }
    193 
    194         // x += digit
    195         if (@add_with_overflow(u64, x, digit, &x)) {
    196             return error.Overflow;
    197         }
    198     }
    199 
    200     return x;
    201 }
    202 
    203 fn char_to_digit(c: u8) -> u8 {
    204     // TODO use switch with range
    205     if ('0' <= c && c <= '9') {
    206         c - '0'
    207     } else if ('A' <= c && c <= 'Z') {
    208         c - 'A' + 10
    209     } else if ('a' <= c && c <= 'z') {
    210         c - 'a' + 10
    211     } else {
    212         @max_value(u8)
    213     }
    214 }
    215 
    216 pub fn buf_print_i64(out_buf: []u8, x: i64) -> isize {
    217     if (x < 0) {
    218         out_buf[0] = '-';
    219         return 1 + buf_print_u64(out_buf[1...], u64(-(x + 1)) + 1);
    220     } else {
    221         return buf_print_u64(out_buf, u64(x));
    222     }
    223 }
    224 
    225 pub fn buf_print_u64(out_buf: []u8, x: u64) -> isize {
    226     var buf: [max_u64_base10_digits]u8 = undefined;
    227     var a = x;
    228     var index: isize = buf.len;
    229 
    230     while (true) {
    231         const digit = a % 10;
    232         index -= 1;
    233         buf[index] = '0' + u8(digit);
    234         a /= 10;
    235         if (a == 0)
    236             break;
    237     }
    238 
    239     const len = buf.len - index;
    240 
    241     @memcpy(&out_buf[0], &buf[index], len);
    242 
    243     return len;
    244 }
    245 
    246 pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize {
    247     const numExpBits = 11;
    248     const numRawSigBits = 52; // not including implicit 1 bit
    249     const expBias = 1023;
    250 
    251     var decs = decimals;
    252     if (decs >= max_u64_base10_digits) {
    253         decs = max_u64_base10_digits - 1;
    254     }
    255 
    256     if (x == f64_get_pos_inf()) {
    257         const buf2 = "+Inf";
    258         @memcpy(&out_buf[0], &buf2[0], buf2.len);
    259         return 4;
    260     } else if (x == f64_get_neg_inf()) {
    261         const buf2 = "-Inf";
    262         @memcpy(&out_buf[0], &buf2[0], buf2.len);
    263         return 4;
    264     } else if (f64_is_nan(x)) {
    265         const buf2 = "NaN";
    266         @memcpy(&out_buf[0], &buf2[0], buf2.len);
    267         return 3;
    268     }
    269 
    270     var buf: [max_f64_digits]u8 = undefined;
    271 
    272     var len: isize = 0;
    273 
    274     // 1 sign bit
    275     // 11 exponent bits
    276     // 52 significand bits (+ 1 implicit always non-zero bit)
    277 
    278     const bits = f64_to_bits(x);
    279     if (bits & (1 << 63) != 0) {
    280         buf[0] = '-';
    281         len += 1;
    282     }
    283 
    284     const rexponent: i64 = i64((bits >> numRawSigBits) & ((1 << numExpBits) - 1));
    285     const exponent = rexponent - expBias - numRawSigBits;
    286 
    287     if (rexponent == 0) {
    288         buf[len] = '0';
    289         len += 1;
    290         @memcpy(&out_buf[0], &buf[0], len);
    291         return len;
    292     }
    293 
    294     const sig = (bits & ((1 << numRawSigBits) - 1)) | (1 << numRawSigBits);
    295 
    296     if (exponent >= 0) {
    297         // number is an integer
    298 
    299         if (exponent >= 64 - 53) {
    300             // use XeX form
    301 
    302             // TODO support printing large floats
    303             //len += buf_print_u64(buf[len...], sig << 10);
    304             const str = "LARGEF64";
    305             @memcpy(&buf[len], &str[0], str.len);
    306             len += str.len;
    307         } else {
    308             // use typical form
    309 
    310             len += buf_print_u64(buf[len...], sig << u64(exponent));
    311             buf[len] = '.';
    312             len += 1;
    313 
    314             var i: isize = 0;
    315             while (i < decs) {
    316                 buf[len] = '0';
    317                 len += 1;
    318                 i += 1;
    319             }
    320         }
    321     } else {
    322         // number is not an integer
    323 
    324         // print out whole part
    325         len += buf_print_u64(buf[len...], sig >> u64(-exponent));
    326         buf[len] = '.';
    327         len += 1;
    328 
    329         // print out fractional part
    330         // dec_num holds: fractional part * 10 ^ decs
    331         var dec_num: u64 = 0;
    332 
    333         var a: isize = 1;
    334         var i: isize = 0;
    335         while (i < decs + 5) {
    336             a *= 10;
    337             i += 1;
    338         }
    339 
    340         // create a mask: 1's for the fractional part, 0's for whole part
    341         var masked_sig = sig & ((1 << u64(-exponent)) - 1);
    342         i = -1;
    343         while (i >= exponent) {
    344             var bit_set = ((1 << u64(i-exponent)) & masked_sig) != 0;
    345 
    346             if (bit_set) {
    347                 dec_num += usize(a) >> usize(-i);
    348             }
    349 
    350             i -= 1;
    351         }
    352 
    353         dec_num /= 100000;
    354 
    355         len += decs;
    356 
    357         i = len - 1;
    358         while (i >= len - decs) {
    359             buf[i] = '0' + u8(dec_num % 10);
    360             dec_num /= 10;
    361             i -= 1;
    362         }
    363     }
    364 
    365     @memcpy(&out_buf[0], &buf[0], len);
    366 
    367     len
    368 }
    369 
    370 #attribute("test")
    371 fn parse_u64_digit_too_big() {
    372     parse_u64("123a", 10) %% |err| {
    373         if (err == error.InvalidChar) return;
    374         unreachable{};
    375     };
    376     unreachable{};
    377 }