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 }