blob 8fc2092f (21843B) - Raw
1 const system = switch(@compileVar("os")) { 2 Os.linux => @import("linux.zig"), 3 Os.darwin => @import("darwin.zig"), 4 else => @compileError("Unsupported OS"), 5 }; 6 7 const errno = @import("errno.zig"); 8 const math = @import("math.zig"); 9 const debug = @import("debug.zig"); 10 const assert = debug.assert; 11 const os = @import("os.zig"); 12 const mem = @import("mem.zig"); 13 14 pub const stdin_fileno = 0; 15 pub const stdout_fileno = 1; 16 pub const stderr_fileno = 2; 17 18 pub var stdin = InStream { 19 .fd = stdin_fileno, 20 }; 21 22 pub var stdout = OutStream { 23 .fd = stdout_fileno, 24 .buffer = undefined, 25 .index = 0, 26 }; 27 28 pub var stderr = OutStream { 29 .fd = stderr_fileno, 30 .buffer = undefined, 31 .index = 0, 32 }; 33 34 /// The function received invalid input at runtime. An Invalid error means a 35 /// bug in the program that called the function. 36 error Invalid; 37 38 /// When an Unexpected error occurs, code that emitted the error likely needs 39 /// a patch to recognize the unexpected case so that it can handle it and emit 40 /// a more specific error. 41 error Unexpected; 42 43 error DiskQuota; 44 error FileTooBig; 45 error Io; 46 error NoSpaceLeft; 47 error BadPerm; 48 error PipeFail; 49 error BadFd; 50 error IsDir; 51 error NotDir; 52 error SymLinkLoop; 53 error ProcessFdQuotaExceeded; 54 error SystemFdQuotaExceeded; 55 error NameTooLong; 56 error NoDevice; 57 error PathNotFound; 58 error NoMem; 59 error Unseekable; 60 error Eof; 61 62 const buffer_size = 4 * 1024; 63 const max_f64_digits = 65; 64 const max_int_digits = 65; 65 66 pub const OpenRead = 0b0001; 67 pub const OpenWrite = 0b0010; 68 pub const OpenCreate = 0b0100; 69 pub const OpenTruncate = 0b1000; 70 71 pub const OutStream = struct { 72 fd: i32, 73 buffer: [buffer_size]u8, 74 index: usize, 75 76 pub fn writeByte(self: &OutStream, b: u8) -> %void { 77 if (self.buffer.len == self.index) %return self.flush(); 78 self.buffer[self.index] = b; 79 self.index += 1; 80 } 81 82 pub fn write(self: &OutStream, bytes: []const u8) -> %void { 83 var src_bytes_left = bytes.len; 84 var src_index: usize = 0; 85 const dest_space_left = self.buffer.len - self.index; 86 87 while (src_bytes_left > 0) { 88 const copy_amt = math.min(dest_space_left, src_bytes_left); 89 @memcpy(&self.buffer[self.index], &bytes[src_index], copy_amt); 90 self.index += copy_amt; 91 if (self.index == self.buffer.len) { 92 %return self.flush(); 93 } 94 src_bytes_left -= copy_amt; 95 } 96 } 97 98 const State = enum { // TODO put inside printf function and make sure the name and debug info is correct 99 Start, 100 OpenBrace, 101 CloseBrace, 102 Integer, 103 IntegerWidth, 104 Character, 105 }; 106 107 /// Calls print and then flushes the buffer. 108 pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void { 109 comptime var start_index = 0; 110 comptime var state = State.Start; 111 comptime var next_arg = 0; 112 comptime var radix = 0; 113 comptime var uppercase = false; 114 comptime var width = 0; 115 comptime var width_start = 0; 116 117 inline for (format) |c, i| { 118 switch (state) { 119 State.Start => switch (c) { 120 '{' => { 121 if (start_index < i) %return self.write(format[start_index...i]); 122 state = State.OpenBrace; 123 }, 124 '}' => { 125 if (start_index < i) %return self.write(format[start_index...i]); 126 state = State.CloseBrace; 127 }, 128 else => {}, 129 }, 130 State.OpenBrace => switch (c) { 131 '{' => { 132 state = State.Start; 133 start_index = i; 134 }, 135 '}' => { 136 %return self.printValue(args[next_arg]); 137 next_arg += 1; 138 state = State.Start; 139 start_index = i + 1; 140 }, 141 'd' => { 142 radix = 10; 143 uppercase = false; 144 width = 0; 145 state = State.Integer; 146 }, 147 'x' => { 148 radix = 16; 149 uppercase = false; 150 width = 0; 151 state = State.Integer; 152 }, 153 'X' => { 154 radix = 16; 155 uppercase = true; 156 width = 0; 157 state = State.Integer; 158 }, 159 'c' => { 160 state = State.Character; 161 }, 162 else => @compileError("Unknown format character: " ++ []u8{c}), 163 }, 164 State.CloseBrace => switch (c) { 165 '}' => { 166 state = State.Start; 167 start_index = i; 168 }, 169 else => @compileError("Single '}' encountered in format string"), 170 }, 171 State.Integer => switch (c) { 172 '}' => { 173 self.printInt(args[next_arg], radix, uppercase, width); 174 next_arg += 1; 175 state = State.Start; 176 start_index = i + 1; 177 }, 178 '0' ... '9' => { 179 width_start = i; 180 state = State.IntegerWidth; 181 }, 182 else => @compileError("Unexpected character in format string: " ++ []u8{c}), 183 }, 184 State.IntegerWidth => switch (c) { 185 '}' => { 186 width = comptime %%parseUnsigned(usize, format[width_start...i], 10); 187 self.printInt(args[next_arg], radix, uppercase, width); 188 next_arg += 1; 189 state = State.Start; 190 start_index = i + 1; 191 }, 192 '0' ... '9' => {}, 193 else => @compileError("Unexpected character in format string: " ++ []u8{c}), 194 }, 195 State.Character => switch (c) { 196 '}' => { 197 self.printAsciiChar(args[next_arg]); 198 next_arg += 1; 199 state = State.Start; 200 start_index = i + 1; 201 }, 202 else => @compileError("Unexpected character in format string: " ++ []u8{c}), 203 }, 204 } 205 } 206 comptime { 207 if (args.len != next_arg) { 208 @compileError("Unused arguments"); 209 } 210 if (state != State.Start) { 211 @compileError("Incomplete format string: " ++ format); 212 } 213 } 214 if (start_index < format.len) { 215 %return self.write(format[start_index...format.len]); 216 } 217 %return self.flush(); 218 } 219 220 pub fn printValue(self: &OutStream, value: var) -> %void { 221 const T = @typeOf(value); 222 if (@isInteger(T)) { 223 return self.printInt(value, 10, false, 0); 224 } else if (@isFloat(T)) { 225 return self.printFloat(T, value); 226 } else if (@canImplicitCast([]const u8, value)) { 227 const casted_value = ([]const u8)(value); 228 return self.write(casted_value); 229 } else if (T == void) { 230 return self.write("void"); 231 } else { 232 @compileError("Unable to print type '" ++ @typeName(T) ++ "'"); 233 } 234 } 235 236 pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool, width: usize) -> %void { 237 if (self.index + max_int_digits >= self.buffer.len) { 238 %return self.flush(); 239 } 240 const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase, width); 241 self.index += amt_printed; 242 } 243 244 pub fn printAsciiChar(self: &OutStream, c: u8) -> %void { 245 if (self.index + 1 >= self.buffer.len) { 246 %return self.flush(); 247 } 248 self.buffer[self.index] = c; 249 self.index += 1; 250 } 251 252 pub fn flush(self: &OutStream) -> %void { 253 while (true) { 254 const write_ret = system.write(self.fd, &self.buffer[0], self.index); 255 const write_err = system.getErrno(write_ret); 256 if (write_err > 0) { 257 return switch (write_err) { 258 errno.EINTR => continue, 259 errno.EINVAL => @unreachable(), 260 errno.EDQUOT => error.DiskQuota, 261 errno.EFBIG => error.FileTooBig, 262 errno.EIO => error.Io, 263 errno.ENOSPC => error.NoSpaceLeft, 264 errno.EPERM => error.BadPerm, 265 errno.EPIPE => error.PipeFail, 266 else => error.Unexpected, 267 } 268 } 269 self.index = 0; 270 return; 271 } 272 } 273 274 pub fn close(self: &OutStream) { 275 while (true) { 276 const close_ret = system.close(self.fd); 277 const close_err = system.getErrno(close_ret); 278 if (close_err > 0 && close_err == errno.EINTR) 279 continue; 280 return; 281 } 282 } 283 }; 284 285 // TODO created a BufferedInStream struct and move some of this code there 286 // BufferedInStream API goes on top of minimal InStream API. 287 pub const InStream = struct { 288 fd: i32, 289 290 /// Call close to clean up. 291 pub fn open(is: &InStream, path: []const u8) -> %void { 292 switch (@compileVar("os")) { 293 Os.linux, Os.darwin => { 294 while (true) { 295 const result = system.open(path, system.O_LARGEFILE|system.O_RDONLY, 0); 296 const err = system.getErrno(result); 297 if (err > 0) { 298 return switch (err) { 299 errno.EINTR => continue, 300 301 errno.EFAULT => @unreachable(), 302 errno.EINVAL => @unreachable(), 303 errno.EACCES => error.BadPerm, 304 errno.EFBIG, errno.EOVERFLOW => error.FileTooBig, 305 errno.EISDIR => error.IsDir, 306 errno.ELOOP => error.SymLinkLoop, 307 errno.EMFILE => error.ProcessFdQuotaExceeded, 308 errno.ENAMETOOLONG => error.NameTooLong, 309 errno.ENFILE => error.SystemFdQuotaExceeded, 310 errno.ENODEV => error.NoDevice, 311 errno.ENOENT => error.PathNotFound, 312 errno.ENOMEM => error.NoMem, 313 errno.ENOSPC => error.NoSpaceLeft, 314 errno.ENOTDIR => error.NotDir, 315 errno.EPERM => error.BadPerm, 316 else => error.Unexpected, 317 } 318 } 319 is.fd = i32(result); 320 return; 321 } 322 }, 323 else => @compileError("unsupported OS"), 324 } 325 326 } 327 328 /// Upon success, the stream is in an uninitialized state. To continue using it, 329 /// you must use the open() function. 330 pub fn close(is: &InStream) -> %void { 331 switch (@compileVar("os")) { 332 Os.linux, Os.darwin => { 333 while (true) { 334 const close_ret = system.close(is.fd); 335 const close_err = system.getErrno(close_ret); 336 if (close_err > 0) { 337 return switch (close_err) { 338 errno.EINTR => continue, 339 340 errno.EIO => error.Io, 341 errno.EBADF => error.BadFd, 342 else => error.Unexpected, 343 } 344 } 345 return; 346 } 347 }, 348 else => @compileError("unsupported OS"), 349 } 350 } 351 352 /// Returns the number of bytes read. If the number read is smaller than buf.len, then 353 /// the stream reached End Of File. 354 pub fn read(is: &InStream, buf: []u8) -> %usize { 355 switch (@compileVar("os")) { 356 Os.linux, Os.darwin => { 357 var index: usize = 0; 358 while (index < buf.len) { 359 const amt_read = system.read(is.fd, &buf[index], buf.len - index); 360 const read_err = system.getErrno(amt_read); 361 if (read_err > 0) { 362 switch (read_err) { 363 errno.EINTR => continue, 364 365 errno.EINVAL => @unreachable(), 366 errno.EFAULT => @unreachable(), 367 errno.EBADF => return error.BadFd, 368 errno.EIO => return error.Io, 369 else => return error.Unexpected, 370 } 371 } 372 if (amt_read == 0) return index; 373 index += amt_read; 374 } 375 return index; 376 }, 377 else => @compileError("unsupported OS"), 378 } 379 } 380 381 pub fn readNoEof(is: &InStream, buf: []u8) -> %void { 382 const amt_read = %return is.read(buf); 383 if (amt_read < buf.len) return error.Eof; 384 } 385 386 pub fn readByte(is: &InStream) -> %u8 { 387 var result: [1]u8 = undefined; 388 %return is.readNoEof(result[0...]); 389 return result[0]; 390 } 391 392 pub fn readIntLe(is: &InStream, comptime T: type) -> %T { 393 is.readInt(false, T) 394 } 395 396 pub fn readIntBe(is: &InStream, comptime T: type) -> %T { 397 is.readInt(true, T) 398 } 399 400 pub fn readInt(is: &InStream, is_be: bool, comptime T: type) -> %T { 401 var bytes: [@sizeOf(T)]u8 = undefined; 402 %return is.readNoEof(bytes[0...]); 403 return mem.readInt(bytes, T, is_be); 404 } 405 406 pub fn readVarInt(is: &InStream, is_be: bool, comptime T: type, size: usize) -> %T { 407 assert(size <= @sizeOf(T)); 408 assert(size <= 8); 409 var input_buf: [8]u8 = undefined; 410 const input_slice = input_buf[0...size]; 411 %return is.readNoEof(input_slice); 412 return mem.readInt(input_slice, T, is_be); 413 } 414 415 pub fn seekForward(is: &InStream, amount: usize) -> %void { 416 switch (@compileVar("os")) { 417 Os.linux, Os.darwin => { 418 const result = system.lseek(is.fd, amount, system.SEEK_CUR); 419 const err = system.getErrno(result); 420 if (err > 0) { 421 return switch (err) { 422 errno.EBADF => error.BadFd, 423 errno.EINVAL => error.Unseekable, 424 errno.EOVERFLOW => error.Unseekable, 425 errno.ESPIPE => error.Unseekable, 426 errno.ENXIO => error.Unseekable, 427 else => error.Unexpected, 428 }; 429 } 430 }, 431 else => @compileError("unsupported OS"), 432 } 433 } 434 435 pub fn seekTo(is: &InStream, pos: usize) -> %void { 436 switch (@compileVar("os")) { 437 Os.linux, Os.darwin => { 438 const result = system.lseek(is.fd, pos, system.SEEK_SET); 439 const err = system.getErrno(result); 440 if (err > 0) { 441 return switch (err) { 442 errno.EBADF => error.BadFd, 443 errno.EINVAL => error.Unseekable, 444 errno.EOVERFLOW => error.Unseekable, 445 errno.ESPIPE => error.Unseekable, 446 errno.ENXIO => error.Unseekable, 447 else => error.Unexpected, 448 }; 449 } 450 }, 451 else => @compileError("unsupported OS"), 452 } 453 } 454 455 pub fn getPos(is: &InStream) -> %usize { 456 switch (@compileVar("os")) { 457 Os.linux, Os.darwin => { 458 const result = system.lseek(is.fd, 0, system.SEEK_CUR); 459 const err = system.getErrno(result); 460 if (err > 0) { 461 return switch (err) { 462 errno.EBADF => error.BadFd, 463 errno.EINVAL => error.Unseekable, 464 errno.EOVERFLOW => error.Unseekable, 465 errno.ESPIPE => error.Unseekable, 466 errno.ENXIO => error.Unseekable, 467 else => error.Unexpected, 468 }; 469 } 470 return result; 471 }, 472 else => @compileError("unsupported OS"), 473 } 474 } 475 476 pub fn getEndPos(is: &InStream) -> %usize { 477 var stat: system.stat = undefined; 478 const err = system.getErrno(system.fstat(is.fd, &stat)); 479 if (err > 0) { 480 return switch (err) { 481 errno.EBADF => error.BadFd, 482 errno.ENOMEM => error.NoMem, 483 else => error.Unexpected, 484 } 485 } 486 487 return usize(stat.size); 488 } 489 }; 490 491 pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T { 492 var x: T = 0; 493 494 for (buf) |c| { 495 const digit = %return charToDigit(c, radix); 496 x = %return math.mulOverflow(T, x, radix); 497 x = %return math.addOverflow(T, x, digit); 498 } 499 500 return x; 501 } 502 503 error InvalidChar; 504 fn charToDigit(c: u8, radix: u8) -> %u8 { 505 const value = switch (c) { 506 '0' ... '9' => c - '0', 507 'A' ... 'Z' => c - 'A' + 10, 508 'a' ... 'z' => c - 'a' + 10, 509 else => return error.InvalidChar, 510 }; 511 512 if (value >= radix) 513 return error.InvalidChar; 514 515 return value; 516 } 517 518 fn digitToChar(digit: u8, uppercase: bool) -> u8 { 519 return switch (digit) { 520 0 ... 9 => digit + '0', 521 10 ... 35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), 522 else => @unreachable(), 523 }; 524 } 525 526 /// Guaranteed to not use more than max_int_digits 527 pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { 528 if (@typeOf(x).is_signed) 529 bufPrintSigned(out_buf, x, base, uppercase, width) 530 else 531 bufPrintUnsigned(out_buf, x, base, uppercase, width) 532 } 533 534 fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { 535 const uint = @intType(false, @typeOf(x).bit_count); 536 if (x < 0) { 537 out_buf[0] = '-'; 538 const new_value = uint(-(x + 1)) + 1; 539 const new_width = if (width == 0) 0 else (width - 1); 540 return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width); 541 } else if (width == 0) { 542 return bufPrintUnsigned(out_buf, uint(x), base, uppercase, width); 543 } else { 544 out_buf[0] = '+'; 545 const new_value = uint(x); 546 const new_width = if (width == 0) 0 else (width - 1); 547 return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width); 548 } 549 } 550 551 fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { 552 // max_int_digits accounts for the minus sign. when printing an unsigned 553 // number we don't need to do that. 554 var buf: [max_int_digits - 1]u8 = undefined; 555 var a = x; 556 var index: usize = buf.len; 557 558 while (true) { 559 const digit = a % base; 560 index -= 1; 561 buf[index] = digitToChar(u8(digit), uppercase); 562 a /= base; 563 if (a == 0) 564 break; 565 } 566 567 const src_buf = buf[index...]; 568 const padding = if (width > src_buf.len) (width - src_buf.len) else 0; 569 570 mem.set(u8, out_buf[0...padding], '0'); 571 mem.copy(u8, out_buf[padding...], src_buf); 572 return src_buf.len + padding; 573 } 574 575 pub fn openSelfExe(stream: &InStream) -> %void { 576 switch (@compileVar("os")) { 577 Os.linux => { 578 %return stream.open("/proc/self/exe"); 579 }, 580 Os.darwin => { 581 %%stderr.printf("TODO: openSelfExe on Darwin\n"); 582 os.abort(); 583 }, 584 else => @compileError("unsupported os"), 585 } 586 } 587 588 fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 { 589 return buf[0...bufPrintInt(buf, value, base, uppercase, width)]; 590 } 591 592 fn testParseU64DigitTooBig() { 593 @setFnTest(this); 594 595 parseUnsigned(u64, "123a", 10) %% |err| { 596 if (err == error.InvalidChar) return; 597 @unreachable(); 598 }; 599 @unreachable(); 600 } 601 602 fn testParseUnsignedComptime() { 603 @setFnTest(this); 604 605 comptime { 606 assert(%%parseUnsigned(usize, "2", 10) == 2); 607 } 608 } 609 610 fn testBufPrintInt() { 611 @setFnTest(this); 612 613 var buffer: [max_int_digits]u8 = undefined; 614 const buf = buffer[0...]; 615 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); 616 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); 617 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); 618 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); 619 620 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); 621 622 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); 623 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); 624 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); 625 626 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); 627 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); 628 }