diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2016-02-27 22:06:46 -0700 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2016-03-01 03:13:40 -0700 |
| commit | f1d338194e9a00e56a42da1298f2ac0ed75797df (patch) | |
| tree | 6768d247960a6e8006fbffa00206ce44152c66d5 /std/io.zig | |
| parent | 28fe994a107b4f66d840c50df614504ac2387587 (diff) | |
| download | zig-f1d338194e9a00e56a42da1298f2ac0ed75797df.tar.gz zig-f1d338194e9a00e56a42da1298f2ac0ed75797df.zip | |
rewrite how importing works
* Introduce the concept of packages. Closes #3
* Add support for error notes.
* Introduce `@import` and `@c_import` builtin functions and
remove the `import` and `c_import` top level declarations.
* Introduce the `use` top level declaration.
* Add `--check-unused` parameter to perform semantic
analysis and codegen on all top level declarations, not
just exported ones and ones referenced by exported ones.
* Delete the root export node and add `--library` argument.
Diffstat (limited to 'std/io.zig')
| -rw-r--r-- | std/io.zig | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/std/io.zig b/std/io.zig new file mode 100644 index 0000000000..272459e4e7 --- /dev/null +++ b/std/io.zig @@ -0,0 +1,377 @@ +const syscall = @import("syscall.zig"); +const errno = @import("errno.zig"); +const math = @import("math.zig"); + +pub const stdin_fileno = 0; +pub const stdout_fileno = 1; +pub const stderr_fileno = 2; + +pub var stdin = InStream { + .fd = stdin_fileno, +}; + +pub var stdout = OutStream { + .fd = stdout_fileno, + .buffer = undefined, + .index = 0, +}; + +pub var stderr = OutStream { + .fd = stderr_fileno, + .buffer = undefined, + .index = 0, +}; + +/// The function received invalid input at runtime. An Invalid error means a +/// bug in the program that called the function. +pub error Invalid; + +/// When an Unexpected error occurs, code that emitted the error likely needs +/// a patch to recognize the unexpected case so that it can handle it and emit +/// a more specific error. +pub error Unexpected; + +pub error DiskQuota; +pub error FileTooBig; +pub error SigInterrupt; +pub error Io; +pub error NoSpaceLeft; +pub error BadPerm; +pub error PipeFail; +pub error BadFd; + +const buffer_size = 4 * 1024; +const max_u64_base10_digits = 20; +const max_f64_digits = 65; + +pub struct OutStream { + fd: isize, + buffer: [buffer_size]u8, + index: isize, + + pub fn print_str(os: &OutStream, str: []const u8) -> %isize { + var src_bytes_left = str.len; + var src_index: @typeof(str.len) = 0; + const dest_space_left = os.buffer.len - os.index; + + while (src_bytes_left > 0) { + const copy_amt = math.min_isize(dest_space_left, src_bytes_left); + @memcpy(&os.buffer[os.index], &str[src_index], copy_amt); + os.index += copy_amt; + if (os.index == os.buffer.len) { + %return os.flush(); + } + src_bytes_left -= copy_amt; + } + return str.len; + } + + /// Prints a byte buffer, flushes the buffer, then returns the number of + /// bytes printed. The "f" is for "flush". + pub fn printf(os: &OutStream, str: []const u8) -> %isize { + const byte_count = %return os.print_str(str); + %return os.flush(); + return byte_count; + } + + pub fn print_u64(os: &OutStream, x: u64) -> %isize { + if (os.index + max_u64_base10_digits >= os.buffer.len) { + %return os.flush(); + } + const amt_printed = buf_print_u64(os.buffer[os.index...], x); + os.index += amt_printed; + + return amt_printed; + } + + pub fn print_i64(os: &OutStream, x: i64) -> %isize { + if (os.index + max_u64_base10_digits >= os.buffer.len) { + %return os.flush(); + } + const amt_printed = buf_print_i64(os.buffer[os.index...], x); + os.index += amt_printed; + + return amt_printed; + } + + pub fn print_f64(os: &OutStream, x: f64) -> %isize { + if (os.index + max_f64_digits >= os.buffer.len) { + %return os.flush(); + } + const amt_printed = buf_print_f64(os.buffer[os.index...], x, 4); + os.index += amt_printed; + + return amt_printed; + } + + pub fn flush(os: &OutStream) -> %void { + const amt_written = syscall.write(os.fd, &os.buffer[0], os.index); + os.index = 0; + if (amt_written < 0) { + return switch (-amt_written) { + errno.EINVAL => unreachable{}, + errno.EDQUOT => error.DiskQuota, + errno.EFBIG => error.FileTooBig, + errno.EINTR => error.SigInterrupt, + errno.EIO => error.Io, + errno.ENOSPC => error.NoSpaceLeft, + errno.EPERM => error.BadPerm, + errno.EPIPE => error.PipeFail, + else => error.Unexpected, + } + } + } + + pub fn close(os: &OutStream) -> %void { + const closed = close(os.fd); + if (closed < 0) { + return switch (-closed) { + EIO => error.Io, + EBADF => error.BadFd, + EINTR => error.SigInterrupt, + else => error.Unexpected, + } + } + } +} + +pub struct InStream { + fd: isize, + + pub fn read(is: &InStream, buf: []u8) -> %isize { + const amt_read = syscall.read(is.fd, &buf[0], buf.len); + if (amt_read < 0) { + return switch (-amt_read) { + errno.EINVAL => unreachable{}, + errno.EFAULT => unreachable{}, + errno.EBADF => error.BadFd, + errno.EINTR => error.SigInterrupt, + errno.EIO => error.Io, + else => error.Unexpected, + } + } + return amt_read; + } + + pub fn close(is: &InStream) -> %void { + const closed = close(is.fd); + if (closed < 0) { + return switch (-closed) { + EIO => error.Io, + EBADF => error.BadFd, + EINTR => error.SigInterrupt, + else => error.Unexpected, + } + } + } +} + +#attribute("cold") +pub fn abort() -> unreachable { + syscall.raise(syscall.SIGABRT); + syscall.raise(syscall.SIGKILL); + while (true) {} +} + +pub error InvalidChar; +pub error Overflow; + +pub fn parse_u64(buf: []u8, radix: u8) -> %u64 { + var x : u64 = 0; + + for (buf) |c| { + const digit = char_to_digit(c); + + if (digit >= radix) { + return error.InvalidChar; + } + + // x *= radix + if (@mul_with_overflow(u64, x, radix, &x)) { + return error.Overflow; + } + + // x += digit + if (@add_with_overflow(u64, x, digit, &x)) { + return error.Overflow; + } + } + + return x; +} + +fn char_to_digit(c: u8) -> u8 { + // TODO use switch with range + if ('0' <= c && c <= '9') { + c - '0' + } else if ('A' <= c && c <= 'Z') { + c - 'A' + 10 + } else if ('a' <= c && c <= 'z') { + c - 'a' + 10 + } else { + @max_value(u8) + } +} + +pub fn buf_print_i64(out_buf: []u8, x: i64) -> isize { + if (x < 0) { + out_buf[0] = '-'; + return 1 + buf_print_u64(out_buf[1...], u64(-(x + 1)) + 1); + } else { + return buf_print_u64(out_buf, u64(x)); + } +} + +pub fn buf_print_u64(out_buf: []u8, x: u64) -> isize { + var buf: [max_u64_base10_digits]u8 = undefined; + var a = x; + var index: isize = buf.len; + + while (true) { + const digit = a % 10; + index -= 1; + buf[index] = '0' + u8(digit); + a /= 10; + if (a == 0) + break; + } + + const len = buf.len - index; + + @memcpy(&out_buf[0], &buf[index], len); + + return len; +} + +pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize { + const numExpBits = 11; + const numRawSigBits = 52; // not including implicit 1 bit + const expBias = 1023; + + var decs = decimals; + if (decs >= max_u64_base10_digits) { + decs = max_u64_base10_digits - 1; + } + + if (x == math.f64_get_pos_inf()) { + const buf2 = "+Inf"; + @memcpy(&out_buf[0], &buf2[0], buf2.len); + return 4; + } else if (x == math.f64_get_neg_inf()) { + const buf2 = "-Inf"; + @memcpy(&out_buf[0], &buf2[0], buf2.len); + return 4; + } else if (math.f64_is_nan(x)) { + const buf2 = "NaN"; + @memcpy(&out_buf[0], &buf2[0], buf2.len); + return 3; + } + + var buf: [max_f64_digits]u8 = undefined; + + var len: isize = 0; + + // 1 sign bit + // 11 exponent bits + // 52 significand bits (+ 1 implicit always non-zero bit) + + const bits = math.f64_to_bits(x); + if (bits & (1 << 63) != 0) { + buf[0] = '-'; + len += 1; + } + + const rexponent: i64 = i64((bits >> numRawSigBits) & ((1 << numExpBits) - 1)); + const exponent = rexponent - expBias - numRawSigBits; + + if (rexponent == 0) { + buf[len] = '0'; + len += 1; + @memcpy(&out_buf[0], &buf[0], len); + return len; + } + + const sig = (bits & ((1 << numRawSigBits) - 1)) | (1 << numRawSigBits); + + if (exponent >= 0) { + // number is an integer + + if (exponent >= 64 - 53) { + // use XeX form + + // TODO support printing large floats + //len += buf_print_u64(buf[len...], sig << 10); + const str = "LARGEF64"; + @memcpy(&buf[len], &str[0], str.len); + len += str.len; + } else { + // use typical form + + len += buf_print_u64(buf[len...], sig << u64(exponent)); + buf[len] = '.'; + len += 1; + + var i: isize = 0; + while (i < decs) { + buf[len] = '0'; + len += 1; + i += 1; + } + } + } else { + // number is not an integer + + // print out whole part + len += buf_print_u64(buf[len...], sig >> u64(-exponent)); + buf[len] = '.'; + len += 1; + + // print out fractional part + // dec_num holds: fractional part * 10 ^ decs + var dec_num: u64 = 0; + + var a: isize = 1; + var i: isize = 0; + while (i < decs + 5) { + a *= 10; + i += 1; + } + + // create a mask: 1's for the fractional part, 0's for whole part + var masked_sig = sig & ((1 << u64(-exponent)) - 1); + i = -1; + while (i >= exponent) { + var bit_set = ((1 << u64(i-exponent)) & masked_sig) != 0; + + if (bit_set) { + dec_num += usize(a) >> usize(-i); + } + + i -= 1; + } + + dec_num /= 100000; + + len += decs; + + i = len - 1; + while (i >= len - decs) { + buf[i] = '0' + u8(dec_num % 10); + dec_num /= 10; + i -= 1; + } + } + + @memcpy(&out_buf[0], &buf[0], len); + + len +} + +#attribute("test") +fn parse_u64_digit_too_big() { + parse_u64("123a", 10) %% |err| { + if (err == error.InvalidChar) return; + unreachable{}; + }; + unreachable{}; +} |
