v2 / vlib / builtin / printing.c.v
223 lines · 212 sloc · 6.68 KB · 881f3b8c1b8e5fae14b2a94212d8be110cd7ca74
Raw
1module builtin
2
3fn 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]
8fn 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 ?]
14pub 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 ?]
39pub 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() .
63pub 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.
77pub 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 .
105pub 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]
121pub 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]
140pub 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]
161fn _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]
179fn _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