231 lines
6.9 KiB
V
231 lines
6.9 KiB
V
module builtin
|
|
|
|
fn C.setvbuf(fstream &C.FILE, buffer &char, mode i32, size usize) i32
|
|
|
|
// set_stream_unbuffered switches a stdio stream to unbuffered mode without using the
|
|
// deprecated `setbuf` API on Windows toolchains.
|
|
@[unsafe]
|
|
fn set_stream_unbuffered(stream &C.FILE) {
|
|
C.setvbuf(stream, &char(nil), C._IONBF, usize(0))
|
|
}
|
|
|
|
// eprintln prints a message with a line end, to stderr. Both stderr and stdout are flushed.
|
|
@[if !noeprintln ?]
|
|
pub fn eprintln(s string) {
|
|
$if builtin_print_use_fprintf ? {
|
|
C.fprintf(C.stderr, c'%.*s\n', s.len, s.str)
|
|
return
|
|
}
|
|
$if freestanding {
|
|
// flushing is only a thing with C.FILE from stdio.h, not on the syscall level
|
|
bare_eprint(s.str, u64(s.len))
|
|
bare_eprint(c'\n', 1)
|
|
} $else $if ios {
|
|
C.WrappedNSLog(s.str)
|
|
} $else {
|
|
flush_stdout()
|
|
flush_stderr()
|
|
// eprintln is used in panics, so it should not fail at all
|
|
$if android && !termux {
|
|
C.android_print(C.stderr, c'%.*s\n', s.len, s.str)
|
|
}
|
|
_writeln_to_fd(2, s)
|
|
flush_stderr()
|
|
}
|
|
}
|
|
|
|
// eprint prints a message to stderr. Both stderr and stdout are flushed.
|
|
@[if !noeprintln ?]
|
|
pub fn eprint(s string) {
|
|
$if builtin_print_use_fprintf ? {
|
|
C.fprintf(C.stderr, c'%.*s', s.len, s.str)
|
|
return
|
|
}
|
|
$if freestanding {
|
|
// flushing is only a thing with C.FILE from stdio.h, not on the syscall level
|
|
bare_eprint(s.str, u64(s.len))
|
|
} $else $if ios {
|
|
// TODO: Implement a buffer as NSLog doesn't have a "print"
|
|
C.WrappedNSLog(s.str)
|
|
} $else {
|
|
flush_stdout()
|
|
flush_stderr()
|
|
$if android && !termux {
|
|
C.android_print(C.stderr, c'%.*s', s.len, s.str)
|
|
}
|
|
_write_buf_to_fd(2, s.str, s.len)
|
|
flush_stderr()
|
|
}
|
|
}
|
|
|
|
// flush_stdout flushes the stdout buffer, ensuring all remaining data is written.
|
|
// See also unbuffer_stdout() .
|
|
pub fn flush_stdout() {
|
|
$if v2_native_windows_pe_minimal ? {
|
|
return
|
|
} $else $if freestanding {
|
|
not_implemented := 'flush_stdout is not implemented\n'
|
|
bare_eprint(not_implemented.str, u64(not_implemented.len))
|
|
} $else $if builtin_write_buf_to_fd_should_use_c_write ? {
|
|
// Native backends do not reliably resolve C.stdout/C.stderr data symbols.
|
|
// Flush all libc output streams without referencing those globals directly.
|
|
C.fflush(unsafe { nil })
|
|
} $else {
|
|
C.fflush(C.stdout)
|
|
}
|
|
}
|
|
|
|
// flush_stderr flushes the stderr buffer, ensuring all remaining data is written.
|
|
pub fn flush_stderr() {
|
|
$if v2_native_windows_pe_minimal ? {
|
|
return
|
|
} $else $if freestanding {
|
|
not_implemented := 'flush_stderr is not implemented\n'
|
|
bare_eprint(not_implemented.str, u64(not_implemented.len))
|
|
} $else $if builtin_write_buf_to_fd_should_use_c_write ? {
|
|
// Native backends do not reliably resolve C.stdout/C.stderr data symbols.
|
|
// Flush all libc output streams without referencing those globals directly.
|
|
C.fflush(unsafe { nil })
|
|
} $else {
|
|
C.fflush(C.stderr)
|
|
}
|
|
}
|
|
|
|
// unbuffer_stdout will turn off the default buffering done for stdout.
|
|
// It will affect all consequent print and println calls, effectively making them behave like
|
|
// eprint and eprintln do. It is useful for programs, that want to produce progress bars, without
|
|
// cluttering your code with a flush_stdout() call after every print() call. It is also useful for
|
|
// programs (sensors), that produce small chunks of output, that you want to be able to process
|
|
// immediately.
|
|
// Note 1: if used, *it should be called at the start of your program*, before using
|
|
// print or println().
|
|
// Note 2: most libc implementations, have logic that use line buffering for stdout, when the output
|
|
// stream is connected to an interactive device, like a terminal, and otherwise fully buffer it,
|
|
// which is good for the output performance for programs that can produce a lot of output (like
|
|
// filters, or cat etc), but bad for latency. V uses unbuffered stdout by default on the common C
|
|
// backends, to make print and println visible immediately even when stdout is redirected.
|
|
// See https://www.gnu.org/software/libc/manual/html_node/Buffering-Concepts.html .
|
|
// See https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05 .
|
|
pub fn unbuffer_stdout() {
|
|
$if vinix {
|
|
return
|
|
} $else $if freestanding {
|
|
not_implemented := 'unbuffer_stdout is not implemented\n'
|
|
bare_eprint(not_implemented.str, u64(not_implemented.len))
|
|
} $else $if builtin_write_buf_to_fd_should_use_c_write ? {
|
|
// Native backends already print via write(), so there is no stdio buffer to adjust here.
|
|
return
|
|
} $else {
|
|
unsafe { set_stream_unbuffered(C.stdout) }
|
|
}
|
|
}
|
|
|
|
// print prints a message to stdout.
|
|
@[manualfree]
|
|
pub fn print(s string) {
|
|
$if builtin_print_use_fprintf ? {
|
|
C.fprintf(C.stdout, c'%.*s', s.len, s.str)
|
|
return
|
|
}
|
|
$if android && !termux {
|
|
C.android_print(C.stdout, c'%.*s\n', s.len, s.str)
|
|
} $else $if ios {
|
|
// TODO: Implement a buffer as NSLog doesn't have a "print"
|
|
C.WrappedNSLog(s.str)
|
|
} $else $if freestanding {
|
|
bare_print(s.str, u64(s.len))
|
|
} $else {
|
|
_write_buf_to_fd(1, s.str, s.len)
|
|
}
|
|
}
|
|
|
|
// println prints a message with a line end, to stdout.
|
|
@[if !noprintln ?; manualfree]
|
|
pub fn println(s string) {
|
|
$if builtin_print_use_fprintf ? {
|
|
C.fprintf(C.stdout, c'%.*s\n', s.len, s.str)
|
|
return
|
|
}
|
|
$if android && !termux {
|
|
C.android_print(C.stdout, c'%.*s\n', s.len, s.str)
|
|
return
|
|
} $else $if ios {
|
|
C.WrappedNSLog(s.str)
|
|
return
|
|
} $else $if freestanding {
|
|
bare_print(s.str, u64(s.len))
|
|
bare_print(c'\n', 1)
|
|
return
|
|
} $else {
|
|
_writeln_to_fd(1, s)
|
|
}
|
|
}
|
|
|
|
@[manualfree]
|
|
fn _writeln_to_fd(fd int, s string) {
|
|
$if builtin_writeln_should_write_at_once ? {
|
|
unsafe {
|
|
buf_len := s.len + 1 // space for \n
|
|
mut buf := malloc(buf_len)
|
|
C.memcpy(buf, s.str, s.len)
|
|
buf[s.len] = `\n`
|
|
_write_buf_to_fd(fd, buf, buf_len)
|
|
free(buf)
|
|
}
|
|
} $else {
|
|
lf := u8(`\n`)
|
|
_write_buf_to_fd(fd, s.str, s.len)
|
|
_write_buf_to_fd(fd, &lf, 1)
|
|
}
|
|
}
|
|
|
|
@[manualfree]
|
|
fn _write_buf_to_fd(fd int, buf &u8, buf_len int) {
|
|
if buf_len <= 0 {
|
|
return
|
|
}
|
|
$if windows {
|
|
$if v2_native_windows_pe_minimal ? {
|
|
write_buf_to_fd_kernel32_or_exit(fd, buf, buf_len)
|
|
} $else {
|
|
write_buf_to_fd_windows_non_minimal(fd, buf, buf_len)
|
|
}
|
|
} $else {
|
|
mut ptr := unsafe { buf }
|
|
mut remaining_bytes := isize(buf_len)
|
|
mut x := isize(0)
|
|
$if freestanding || vinix || builtin_write_buf_to_fd_should_use_c_write ? {
|
|
// Flush any pending libc stdio output (from C.puts, C.putchar, etc.)
|
|
// before writing directly via write() syscall to prevent output reordering.
|
|
C.fflush(unsafe { nil })
|
|
unsafe {
|
|
for remaining_bytes > 0 {
|
|
x = C.write(fd, ptr, remaining_bytes)
|
|
if x <= 0 {
|
|
// Detached/invalid stdio must not trap the process in an infinite loop.
|
|
break
|
|
}
|
|
ptr += x
|
|
remaining_bytes -= x
|
|
}
|
|
}
|
|
} $else {
|
|
mut stream := voidptr(C.stdout)
|
|
if fd == 2 {
|
|
stream = voidptr(C.stderr)
|
|
}
|
|
unsafe {
|
|
for remaining_bytes > 0 {
|
|
x = isize(C.fwrite(ptr, 1, remaining_bytes, stream))
|
|
if x <= 0 {
|
|
// GUI programs on Windows may not have a writable stdout/stderr stream.
|
|
break
|
|
}
|
|
ptr += x
|
|
remaining_bytes -= x
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|