v / vlib / builtin / printing.c.v
230 lines · 219 sloc · 6.93 KB · 81a5657604ec6da99c25e26546870c6888d6fdde
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 v2_native_windows_pe_minimal ? {
65 return
66 } $else $if freestanding {
67 not_implemented := 'flush_stdout is not implemented\n'
68 bare_eprint(not_implemented.str, u64(not_implemented.len))
69 } $else $if builtin_write_buf_to_fd_should_use_c_write ? {
70 // Native backends do not reliably resolve C.stdout/C.stderr data symbols.
71 // Flush all libc output streams without referencing those globals directly.
72 C.fflush(unsafe { nil })
73 } $else {
74 C.fflush(C.stdout)
75 }
76}
77
78// flush_stderr flushes the stderr buffer, ensuring all remaining data is written.
79pub fn flush_stderr() {
80 $if v2_native_windows_pe_minimal ? {
81 return
82 } $else $if freestanding {
83 not_implemented := 'flush_stderr is not implemented\n'
84 bare_eprint(not_implemented.str, u64(not_implemented.len))
85 } $else $if builtin_write_buf_to_fd_should_use_c_write ? {
86 // Native backends do not reliably resolve C.stdout/C.stderr data symbols.
87 // Flush all libc output streams without referencing those globals directly.
88 C.fflush(unsafe { nil })
89 } $else {
90 C.fflush(C.stderr)
91 }
92}
93
94// unbuffer_stdout will turn off the default buffering done for stdout.
95// It will affect all consequent print and println calls, effectively making them behave like
96// eprint and eprintln do. It is useful for programs, that want to produce progress bars, without
97// cluttering your code with a flush_stdout() call after every print() call. It is also useful for
98// programs (sensors), that produce small chunks of output, that you want to be able to process
99// immediately.
100// Note 1: if used, *it should be called at the start of your program*, before using
101// print or println().
102// Note 2: most libc implementations, have logic that use line buffering for stdout, when the output
103// stream is connected to an interactive device, like a terminal, and otherwise fully buffer it,
104// which is good for the output performance for programs that can produce a lot of output (like
105// filters, or cat etc), but bad for latency. V uses unbuffered stdout by default on the common C
106// backends, to make print and println visible immediately even when stdout is redirected.
107// See https://www.gnu.org/software/libc/manual/html_node/Buffering-Concepts.html .
108// See https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05 .
109pub fn unbuffer_stdout() {
110 $if vinix {
111 return
112 } $else $if freestanding {
113 not_implemented := 'unbuffer_stdout is not implemented\n'
114 bare_eprint(not_implemented.str, u64(not_implemented.len))
115 } $else $if builtin_write_buf_to_fd_should_use_c_write ? {
116 // Native backends already print via write(), so there is no stdio buffer to adjust here.
117 return
118 } $else {
119 unsafe { set_stream_unbuffered(C.stdout) }
120 }
121}
122
123// print prints a message to stdout.
124@[manualfree]
125pub fn print(s string) {
126 $if builtin_print_use_fprintf ? {
127 C.fprintf(C.stdout, c'%.*s', s.len, s.str)
128 return
129 }
130 $if android && !termux {
131 C.android_print(C.stdout, c'%.*s\n', s.len, s.str)
132 } $else $if ios {
133 // TODO: Implement a buffer as NSLog doesn't have a "print"
134 C.WrappedNSLog(s.str)
135 } $else $if freestanding {
136 bare_print(s.str, u64(s.len))
137 } $else {
138 _write_buf_to_fd(1, s.str, s.len)
139 }
140}
141
142// println prints a message with a line end, to stdout.
143@[if !noprintln ?; manualfree]
144pub fn println(s string) {
145 $if builtin_print_use_fprintf ? {
146 C.fprintf(C.stdout, c'%.*s\n', s.len, s.str)
147 return
148 }
149 $if android && !termux {
150 C.android_print(C.stdout, c'%.*s\n', s.len, s.str)
151 return
152 } $else $if ios {
153 C.WrappedNSLog(s.str)
154 return
155 } $else $if freestanding {
156 bare_print(s.str, u64(s.len))
157 bare_print(c'\n', 1)
158 return
159 } $else {
160 _writeln_to_fd(1, s)
161 }
162}
163
164@[manualfree]
165fn _writeln_to_fd(fd int, s string) {
166 $if builtin_writeln_should_write_at_once ? {
167 unsafe {
168 buf_len := s.len + 1 // space for \n
169 mut buf := malloc(buf_len)
170 C.memcpy(buf, s.str, s.len)
171 buf[s.len] = `\n`
172 _write_buf_to_fd(fd, buf, buf_len)
173 free(buf)
174 }
175 } $else {
176 lf := u8(`\n`)
177 _write_buf_to_fd(fd, s.str, s.len)
178 _write_buf_to_fd(fd, &lf, 1)
179 }
180}
181
182@[manualfree]
183fn _write_buf_to_fd(fd int, buf &u8, buf_len int) {
184 if buf_len <= 0 {
185 return
186 }
187 $if windows {
188 $if v2_native_windows_pe_minimal ? {
189 write_buf_to_fd_kernel32_or_exit(fd, buf, buf_len)
190 } $else {
191 write_buf_to_fd_windows_non_minimal(fd, buf, buf_len)
192 }
193 } $else {
194 mut ptr := unsafe { buf }
195 mut remaining_bytes := isize(buf_len)
196 mut x := isize(0)
197 $if freestanding || vinix || builtin_write_buf_to_fd_should_use_c_write ? {
198 // Flush any pending libc stdio output (from C.puts, C.putchar, etc.)
199 // before writing directly via write() syscall to prevent output reordering.
200 C.fflush(unsafe { nil })
201 unsafe {
202 for remaining_bytes > 0 {
203 x = C.write(fd, ptr, remaining_bytes)
204 if x <= 0 {
205 // Detached/invalid stdio must not trap the process in an infinite loop.
206 break
207 }
208 ptr += x
209 remaining_bytes -= x
210 }
211 }
212 } $else {
213 mut stream := voidptr(C.stdout)
214 if fd == 2 {
215 stream = voidptr(C.stderr)
216 }
217 unsafe {
218 for remaining_bytes > 0 {
219 x = isize(C.fwrite(ptr, 1, remaining_bytes, stream))
220 if x <= 0 {
221 // GUI programs on Windows may not have a writable stdout/stderr stream.
222 break
223 }
224 ptr += x
225 remaining_bytes -= x
226 }
227 }
228 }
229 }
230}
231