Files
c3c/lib/std/io/io.c3
Christoffer Lerno 3c50376175 New test runner
2025-01-30 01:09:48 +01:00

418 lines
8.6 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::io;
import libc;
enum Seek
{
SET,
CURSOR,
END
}
fault IoError
{
ALREADY_EXISTS,
BUSY,
CANNOT_READ_DIR,
DIR_NOT_EMPTY,
EOF,
FILE_CANNOT_DELETE,
FILE_IS_DIR,
FILE_IS_PIPE,
FILE_NOT_DIR,
FILE_NOT_FOUND,
FILE_NOT_VALID,
GENERAL_ERROR,
ILLEGAL_ARGUMENT,
INCOMPLETE_WRITE,
INTERRUPTED,
INVALID_POSITION,
INVALID_PUSHBACK,
NAME_TOO_LONG,
NOT_SEEKABLE,
NO_PERMISSION,
OUT_OF_SPACE,
OVERFLOW,
READ_ONLY,
SYMLINK_FAILED,
TOO_MANY_DESCRIPTORS,
UNEXPECTED_EOF,
UNKNOWN_ERROR,
UNSUPPORTED_OPERATION,
WOULD_BLOCK,
}
<*
Read from a stream (default is stdin) to the next "\n"
or to the end of the stream, whatever comes first.
"\r" will be filtered from the String.
@param stream `The stream to read from.`
@require @is_instream(stream) `The stream must implement InStream.`
@param [inout] allocator `the allocator to use.`
@return `The string containing the data read.`
*>
macro String! readline(stream = io::stdin(), Allocator allocator = allocator::heap())
{
bool $is_stream = @typeis(stream, InStream);
$if $is_stream:
$typeof(&stream.read_byte) func = &stream.read_byte;
char val = func((void*)stream)!;
$else
char val = stream.read_byte()!;
$endif
if (val == '\n') return "";
@pool(allocator)
{
DString str = dstring::temp_with_capacity(256);
if (val != '\r') str.append(val);
while (1)
{
$if $is_stream:
char! c = func((void*)stream);
$else
char! c = stream.read_byte();
$endif
if (catch err = c)
{
if (err == IoError.EOF) break;
return err?;
}
if (c == '\r') continue;
if (c == '\n') break;
str.append_char(c);
}
return str.copy_str(allocator);
};
}
<*
Reads a string, see `readline`, except the it is allocated
on the temporary allocator and does not need to be freed.
@param stream `The stream to read from.`
@require @is_instream(stream) `The stream must implement InStream.`
@return `The temporary string containing the data read.`
*>
macro String! treadline(stream = io::stdin())
{
return readline(stream, allocator::temp()) @inline;
}
<*
Print a value to a stream.
@param out `the stream to print to`
@param x `the value to print`
@require @is_outstream(out) `The output must implement OutStream.`
@return `the number of bytes printed.`
*>
macro usz! fprint(out, x)
{
var $Type = $typeof(x);
$switch ($Type)
$case String: return out.write(x);
$case ZString: return out.write(x.str_view());
$case DString: return out.write(x.str_view());
$default:
$if $assignable(x, String):
return out.write((String)x);
$else
$if is_struct_with_default_print($Type):
Formatter formatter;
formatter.init(&out_putstream_fn, &&(OutStream)out);
return struct_to_format(x, &formatter, false);
$else
return fprintf(out, "%s", x);
$endif
$endif
$endswitch
}
<*
Prints using a 'printf'-style formatting string.
See `printf` for details on formatting.
@param [inout] out `The OutStream to print to`
@param [in] format `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! fprintf(OutStream out, String format, args...)
{
Formatter formatter;
formatter.init(&out_putstream_fn, &out);
return formatter.vprintf(format, args);
}
<*
Prints using a 'printf'-style formatting string,
appending '\n' at the end. See `printf`.
@param [inout] out `The OutStream to print to`
@param [in] format `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! fprintfn(OutStream out, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putstream_fn, &out);
usz len = formatter.vprintf(format, args)!;
out.write_byte('\n')!;
if (&out.flush) out.flush()!;
return len + 1;
}
<*
@require @is_outstream(out) "The output must implement OutStream"
*>
macro usz! fprintn(out, x = "")
{
usz len = fprint(out, x)!;
out.write_byte('\n')!;
$switch
$case @typeid(out) == OutStream.typeid:
if (&out.flush) out.flush()!;
$case $defined(out.flush):
out.flush()!;
$endswitch
return len + 1;
}
<*
Print any value to stdout.
*>
macro void print(x)
{
(void)fprint(io::stdout(), x);
}
<*
Print any value to stdout, appending an '\n after.
@param x "The value to print"
*>
macro void printn(x = "")
{
(void)fprintn(io::stdout(), x);
}
<*
Print any value to stderr.
*>
macro void eprint(x)
{
(void)fprint(io::stderr(), x);
}
<*
Print any value to stderr, appending an '\n after.
@param x "The value to print"
*>
macro void eprintn(x)
{
(void)fprintn(io::stderr(), x);
}
fn void! out_putstream_fn(void* data, char c) @private
{
OutStream* stream = data;
return (*stream).write_byte(c);
}
fn void! out_putchar_fn(void* data @unused, char c) @private
{
$if env::TESTING:
// HACK: this is used for the purpose of unit test output hijacking
File* stdout = io::stdout();
assert(stdout.file);
libc::fputc(c, stdout.file);
$else
libc::putchar(c);
$endif
}
<*
Prints using a 'printf'-style formatting string.
To print integer numbers, use "%d" or "%x"/"%X,
the latter gives the hexadecimal representation.
All types can be printed using "%s" which gives
the default representation of the value.
To create a custom output for a type, implement
the Printable interface.
@param [in] format `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! printf(String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
return formatter.vprintf(format, args);
}
<*
Prints using a 'printf'-style formatting string,
appending '\n' at the end. See `printf`.
@param [in] format `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! printfn(String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
usz! len = formatter.vprintf(format, args);
out_putchar_fn(null, '\n')!;
io::stdout().flush()!;
return len + 1;
}
<*
Prints using a 'printf'-style formatting string
to stderr.
@param [in] format `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! eprintf(String format, args...) @maydiscard
{
Formatter formatter;
OutStream stream = stderr();
formatter.init(&out_putstream_fn, &stream);
return formatter.vprintf(format, args);
}
<*
Prints using a 'printf'-style formatting string,
to stderr appending '\n' at the end. See `printf`.
@param [in] format `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! eprintfn(String format, args...) @maydiscard
{
Formatter formatter;
OutStream stream = stderr();
formatter.init(&out_putstream_fn, &stream);
usz! len = formatter.vprintf(format, args);
stderr().write_byte('\n')!;
stderr().flush()!;
return len + 1;
}
<*
Prints using a 'printf'-style formatting string,
to a string buffer. See `printf`.
@param [inout] buffer `The buffer to print to`
@param [in] format `The printf-style format string`
@return `a slice formed from the "buffer" with the resulting length.`
*>
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
{
Formatter formatter;
BufferData data = { .buffer = buffer };
formatter.init(&out_buffer_fn, &data);
usz size = formatter.vprintf(format, args)!;
return buffer[:data.written];
}
// Used to print to a buffer.
fn void! out_buffer_fn(void *data, char c) @private
{
BufferData *buffer_data = data;
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?;
buffer_data.buffer[buffer_data.written++] = c;
}
// Used for buffer printing
struct BufferData @private
{
char[] buffer;
usz written;
}
// Only available with LIBC
module std::io @if (env::LIBC);
import libc;
<*
Libc `putchar`, prints a single character to stdout.
*>
fn void putchar(char c) @inline
{
libc::putchar(c);
}
<*
Get standard out.
@return `stdout as a File`
*>
fn File* stdout()
{
static File file;
if (!file.file) file = file::from_handle(libc::stdout());
return &file;
}
<*
Get standard err.
@return `stderr as a File`
*>
fn File* stderr()
{
static File file;
if (!file.file) file = file::from_handle(libc::stderr());
return &file;
}
<*
Get standard in.
@return `stdin as a File`
*>
fn File* stdin()
{
static File file;
if (!file.file) file = file::from_handle(libc::stdin());
return &file;
}
module std::io @if(!env::LIBC);
File stdin_file;
File stdout_file;
File stderr_file;
fn void putchar(char c) @inline
{
(void)stdout_file.write_byte(c);
}
fn File* stdout()
{
return &stdout_file;
}
fn File* stderr()
{
return &stderr_file;
}
fn File* stdin()
{
return &stdin_file;
}