| 1 | module builtin |
| 2 | |
| 3 | fn C.setvbuf(fstream &C.FILE, buffer &char, mode i32, size usize) i32 |
| 4 | |
| 5 | // set_stream_unbuffered switches a stdio stream to unbuffered mode without using the |
| 6 | // deprecated `setbuf` API on Windows toolchains. |
| 7 | @[unsafe] |
| 8 | fn set_stream_unbuffered(stream &C.FILE) { |
| 9 | C.setvbuf(stream, &char(nil), C._IONBF, usize(0)) |
| 10 | } |
| 11 | |
| 12 | // eprintln prints a message with a line end, to stderr. Both stderr and stdout are flushed. |
| 13 | @[if !noeprintln ?] |
| 14 | pub fn eprintln(s string) { |
| 15 | $if builtin_print_use_fprintf ? { |
| 16 | C.fprintf(C.stderr, c'%.*s\n', s.len, s.str) |
| 17 | return |
| 18 | } |
| 19 | $if freestanding { |
| 20 | // flushing is only a thing with C.FILE from stdio.h, not on the syscall level |
| 21 | bare_eprint(s.str, u64(s.len)) |
| 22 | bare_eprint(c'\n', 1) |
| 23 | } $else $if ios { |
| 24 | C.WrappedNSLog(s.str) |
| 25 | } $else { |
| 26 | flush_stdout() |
| 27 | flush_stderr() |
| 28 | // eprintln is used in panics, so it should not fail at all |
| 29 | $if android && !termux { |
| 30 | C.android_print(C.stderr, c'%.*s\n', s.len, s.str) |
| 31 | } |
| 32 | _writeln_to_fd(2, s) |
| 33 | flush_stderr() |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | // eprint prints a message to stderr. Both stderr and stdout are flushed. |
| 38 | @[if !noeprintln ?] |
| 39 | pub fn eprint(s string) { |
| 40 | $if builtin_print_use_fprintf ? { |
| 41 | C.fprintf(C.stderr, c'%.*s', s.len, s.str) |
| 42 | return |
| 43 | } |
| 44 | $if freestanding { |
| 45 | // flushing is only a thing with C.FILE from stdio.h, not on the syscall level |
| 46 | bare_eprint(s.str, u64(s.len)) |
| 47 | } $else $if ios { |
| 48 | // TODO: Implement a buffer as NSLog doesn't have a "print" |
| 49 | C.WrappedNSLog(s.str) |
| 50 | } $else { |
| 51 | flush_stdout() |
| 52 | flush_stderr() |
| 53 | $if android && !termux { |
| 54 | C.android_print(C.stderr, c'%.*s', s.len, s.str) |
| 55 | } |
| 56 | _write_buf_to_fd(2, s.str, s.len) |
| 57 | flush_stderr() |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | // flush_stdout flushes the stdout buffer, ensuring all remaining data is written. |
| 62 | // See also unbuffer_stdout() . |
| 63 | pub fn flush_stdout() { |
| 64 | $if freestanding { |
| 65 | not_implemented := 'flush_stdout is not implemented\n' |
| 66 | bare_eprint(not_implemented.str, u64(not_implemented.len)) |
| 67 | } $else $if builtin_write_buf_to_fd_should_use_c_write ? { |
| 68 | // Native backends do not reliably resolve C.stdout/C.stderr data symbols. |
| 69 | // Flush all libc output streams without referencing those globals directly. |
| 70 | C.fflush(unsafe { nil }) |
| 71 | } $else { |
| 72 | C.fflush(C.stdout) |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | // flush_stderr flushes the stderr buffer, ensuring all remaining data is written. |
| 77 | pub fn flush_stderr() { |
| 78 | $if freestanding { |
| 79 | not_implemented := 'flush_stderr is not implemented\n' |
| 80 | bare_eprint(not_implemented.str, u64(not_implemented.len)) |
| 81 | } $else $if builtin_write_buf_to_fd_should_use_c_write ? { |
| 82 | // Native backends do not reliably resolve C.stdout/C.stderr data symbols. |
| 83 | // Flush all libc output streams without referencing those globals directly. |
| 84 | C.fflush(unsafe { nil }) |
| 85 | } $else { |
| 86 | C.fflush(C.stderr) |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | // unbuffer_stdout will turn off the default buffering done for stdout. |
| 91 | // It will affect all consequent print and println calls, effectively making them behave like |
| 92 | // eprint and eprintln do. It is useful for programs, that want to produce progress bars, without |
| 93 | // cluttering your code with a flush_stdout() call after every print() call. It is also useful for |
| 94 | // programs (sensors), that produce small chunks of output, that you want to be able to process |
| 95 | // immediately. |
| 96 | // Note 1: if used, *it should be called at the start of your program*, before using |
| 97 | // print or println(). |
| 98 | // Note 2: most libc implementations, have logic that use line buffering for stdout, when the output |
| 99 | // stream is connected to an interactive device, like a terminal, and otherwise fully buffer it, |
| 100 | // which is good for the output performance for programs that can produce a lot of output (like |
| 101 | // filters, or cat etc), but bad for latency. V uses unbuffered stdout by default on the common C |
| 102 | // backends, to make print and println visible immediately even when stdout is redirected. |
| 103 | // See https://www.gnu.org/software/libc/manual/html_node/Buffering-Concepts.html . |
| 104 | // See https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05 . |
| 105 | pub fn unbuffer_stdout() { |
| 106 | $if vinix { |
| 107 | return |
| 108 | } $else $if freestanding { |
| 109 | not_implemented := 'unbuffer_stdout is not implemented\n' |
| 110 | bare_eprint(not_implemented.str, u64(not_implemented.len)) |
| 111 | } $else $if builtin_write_buf_to_fd_should_use_c_write ? { |
| 112 | // Native backends already print via write(), so there is no stdio buffer to adjust here. |
| 113 | return |
| 114 | } $else { |
| 115 | unsafe { set_stream_unbuffered(C.stdout) } |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | // print prints a message to stdout. |
| 120 | @[manualfree] |
| 121 | pub fn print(s string) { |
| 122 | $if builtin_print_use_fprintf ? { |
| 123 | C.fprintf(C.stdout, c'%.*s', s.len, s.str) |
| 124 | return |
| 125 | } |
| 126 | $if android && !termux { |
| 127 | C.android_print(C.stdout, c'%.*s\n', s.len, s.str) |
| 128 | } $else $if ios { |
| 129 | // TODO: Implement a buffer as NSLog doesn't have a "print" |
| 130 | C.WrappedNSLog(s.str) |
| 131 | } $else $if freestanding { |
| 132 | bare_print(s.str, u64(s.len)) |
| 133 | } $else { |
| 134 | _write_buf_to_fd(1, s.str, s.len) |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | // println prints a message with a line end, to stdout. |
| 139 | @[if !noprintln ?; manualfree] |
| 140 | pub fn println(s string) { |
| 141 | $if builtin_print_use_fprintf ? { |
| 142 | C.fprintf(C.stdout, c'%.*s\n', s.len, s.str) |
| 143 | return |
| 144 | } |
| 145 | $if android && !termux { |
| 146 | C.android_print(C.stdout, c'%.*s\n', s.len, s.str) |
| 147 | return |
| 148 | } $else $if ios { |
| 149 | C.WrappedNSLog(s.str) |
| 150 | return |
| 151 | } $else $if freestanding { |
| 152 | bare_print(s.str, u64(s.len)) |
| 153 | bare_print(c'\n', 1) |
| 154 | return |
| 155 | } $else { |
| 156 | _writeln_to_fd(1, s) |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | @[manualfree] |
| 161 | fn _writeln_to_fd(fd int, s string) { |
| 162 | $if builtin_writeln_should_write_at_once ? { |
| 163 | unsafe { |
| 164 | buf_len := s.len + 1 // space for \n |
| 165 | mut buf := malloc(buf_len) |
| 166 | C.memcpy(buf, s.str, s.len) |
| 167 | buf[s.len] = `\n` |
| 168 | _write_buf_to_fd(fd, buf, buf_len) |
| 169 | free(buf) |
| 170 | } |
| 171 | } $else { |
| 172 | lf := u8(`\n`) |
| 173 | _write_buf_to_fd(fd, s.str, s.len) |
| 174 | _write_buf_to_fd(fd, &lf, 1) |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | @[manualfree] |
| 179 | fn _write_buf_to_fd(fd int, buf &u8, buf_len int) { |
| 180 | if buf_len <= 0 { |
| 181 | return |
| 182 | } |
| 183 | mut ptr := unsafe { buf } |
| 184 | mut remaining_bytes := isize(buf_len) |
| 185 | mut x := isize(0) |
| 186 | $if windows { |
| 187 | if write_buf_to_console(fd, ptr, int(remaining_bytes)) { |
| 188 | return |
| 189 | } |
| 190 | } |
| 191 | $if freestanding || vinix || builtin_write_buf_to_fd_should_use_c_write ? { |
| 192 | // Flush any pending libc stdio output (from C.puts, C.putchar, etc.) |
| 193 | // before writing directly via write() syscall to prevent output reordering. |
| 194 | C.fflush(unsafe { nil }) |
| 195 | unsafe { |
| 196 | for remaining_bytes > 0 { |
| 197 | x = C.write(fd, ptr, remaining_bytes) |
| 198 | if x <= 0 { |
| 199 | // Detached/invalid stdio must not trap the process in an infinite loop. |
| 200 | break |
| 201 | } |
| 202 | ptr += x |
| 203 | remaining_bytes -= x |
| 204 | } |
| 205 | } |
| 206 | } $else { |
| 207 | mut stream := voidptr(C.stdout) |
| 208 | if fd == 2 { |
| 209 | stream = voidptr(C.stderr) |
| 210 | } |
| 211 | unsafe { |
| 212 | for remaining_bytes > 0 { |
| 213 | x = isize(C.fwrite(ptr, 1, remaining_bytes, stream)) |
| 214 | if x <= 0 { |
| 215 | // GUI programs on Windows may not have a writable stdout/stderr stream. |
| 216 | break |
| 217 | } |
| 218 | ptr += x |
| 219 | remaining_bytes -= x |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | |