Interface based streams. Fix for initializing with a force unwrap inside. Allow $define to take a list. Allow $define to return error on argument type mismatch in call. Fixed broken bit operations on boolean vectors.

This commit is contained in:
Christoffer Lerno
2023-10-30 14:07:37 +01:00
committed by Christoffer Lerno
parent e4c1328ef2
commit 1aa038c92f
42 changed files with 789 additions and 890 deletions

View File

@@ -202,21 +202,21 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
return &page.data[0];
}
fn void TempAllocator.print_pages(&self, File f)
fn void! TempAllocator.print_pages(&self, File f)
{
TempAllocatorPage *last_page = self.last_page;
if (!last_page)
{
f.printf("No pages.\n");
io::fprintf(&f, "No pages.\n")!;
return;
}
f.printf("---Pages----\n");
io::fprintf(&f, "---Pages----\n")!;
uint index = 0;
while (last_page)
{
bool is_not_aligned = !(last_page.size & (1u64 << 63));
f.printf("%d. Alloc: %d %d at %p%s\n", ++index,
last_page.size & ~(1u64 << 63), last_page.mark, &last_page.data[0], is_not_aligned ? "" : " [aligned]");
io::fprintf(&f, "%d. Alloc: %d %d at %p%s\n", ++index,
last_page.size & ~(1u64 << 63), last_page.mark, &last_page.data[0], is_not_aligned ? "" : " [aligned]")!;
last_page = last_page.prev_page;
}
}

View File

@@ -94,33 +94,33 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::DARWI
BacktraceList! backtrace = darwin::backtrace_load(mem::temp());
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
io::printn("'");
io::eprint("\nERROR: '");
io::eprint(message);
io::eprintn("'");
foreach (i, &trace : backtrace)
{
if (i < backtraces_to_ignore) continue;
if (trace.is_unknown())
{
(void)io::stderr().printn(" in ???");
io::eprintn(" in ???");
continue;
}
if (trace.has_file())
{
(void)io::stderr().printfn(" in %s (%s:%d) [%s]", trace.function, trace.file, trace.line, trace.object_file);
io::eprintfn(" in %s (%s:%d) [%s]", trace.function, trace.file, trace.line, trace.object_file);
continue;
}
(void)io::stderr().printfn(" in %s (source unavailable) [%s]", trace.function, trace.object_file);
io::eprintfn(" in %s (source unavailable) [%s]", trace.function, trace.object_file);
}
return true;
};
}
fn void default_panic(String message, String file, String function, uint line) @if(env::DARWIN)
{
$if $defined(io::stderr) && $defined(Stream.printf):
$if $defined(io::stderr):
if (!print_backtrace(message, 2))
{
(void)io::stderr().printfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
return;
}
$endif
@@ -130,23 +130,23 @@ fn void default_panic(String message, String file, String function, uint line) @
fn void default_panic(String message, String file, String function, uint line) @if(!env::DARWIN)
{
CallstackElement* stack = $$stacktrace();
$if $defined(io::stderr) && $defined(Stream.printf):
$if $defined(io::stderr):
if (stack) stack = stack.prev;
if (stack)
{
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
(void)io::stderr().printn("'");
io::eprint("\nERROR: '");
io::eprint(message);
io::eprintn("'");
}
else
{
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
(void)io::stderr().printfn("', in %s (%s:%d)", function, file, line);
io::eprint("\nERROR: '");
io::eprint(message);
io::eprintfn("', in %s (%s:%d)", function, file, line);
}
while (stack)
{
(void)io::stderr().printfn(" in %s %s (%s:%d)", stack.location.name, stack.function, stack.file, stack.line);
io::eprintfn(" in %s %s (%s:%d)", stack.location.name, stack.function, stack.file, stack.line);
if (stack == stack.prev) break;
stack = stack.prev;
}
@@ -398,10 +398,10 @@ fn void sig_bus_error(CInt i)
$if !env::DARWIN:
sig_panic("Illegal memory access.");
$else
$if $defined(io::stderr) && $defined(Stream.printf):
$if $defined(io::stderr):
if (!print_backtrace("Illegal memory access.", 1))
{
(void)io::stderr().printn("\nERROR: 'Illegal memory access'.");
io::eprintn("\nERROR: 'Illegal memory access'.");
}
$endif
$endif
@@ -413,10 +413,10 @@ fn void sig_segmentation_fault(CInt i)
$if !env::DARWIN:
sig_panic("Out of bounds memory access.");
$else
$if $defined(io::stderr) && $defined(Stream.printf):
$if $defined(io::stderr):
if (!print_backtrace("Out of bounds memory access.", 1))
{
(void)io::stderr().printn("\nERROR: Memory error without backtrace, possible stack overflow.");
io::eprintn("\nERROR: Memory error without backtrace, possible stack overflow.");
}
$endif
$endif

View File

@@ -83,9 +83,9 @@ macro greater_eq(a, b) @builtin
macro bool equals(a, b) @builtin
{
$switch
$case $defined(a.equals):
$case $defined(a.equals, a.equals(b)):
return a.equals(b);
$case $defined(a.compare_to):
$case $defined(a.compare_to, a.compare_to(b)):
return a.compare_to(b) == 0;
$case $defined(a.less):
return !a.less(b) && !b.less(a);

View File

@@ -1,6 +1,7 @@
module std::core::dstring;
import std::io;
distinct DString = void*;
distinct DString (OutStream) = void*;
const usz MIN_CAPACITY @private = 16;
@@ -79,9 +80,9 @@ fn usz DString.capacity(self)
return self.data().capacity;
}
fn usz DString.len(self)
fn usz DString.len(&self) @dynamic
{
if (!self) return 0;
if (!*self) return 0;
return self.data().len;
}
@@ -255,6 +256,17 @@ fn void DString.clear(self)
self.data().len = 0;
}
fn usz! DString.write(&self, char[] buffer) @dynamic
{
self.append_chars((String)buffer);
return buffer.len;
}
fn void! DString.write_byte(&self, char c) @dynamic
{
self.append_char(c);
}
fn void DString.append_char(&self, char c)
{
if (!*self)
@@ -387,9 +399,9 @@ fn void DString.reserve(&self, usz addition)
*self = (DString)realloc(data, StringData.sizeof + new_capacity, .using = data.allocator);
}
fn usz! DString.read_from_stream(&self, Stream* reader)
fn usz! DString.read_from_stream(&self, InStream* reader)
{
if (reader.supports_available())
if (&reader.available)
{
usz total_read = 0;
while (usz available = reader.available()!)

View File

@@ -3,11 +3,11 @@ import std::io;
struct CsvReader
{
Stream* stream;
InStream* stream;
String separator;
}
fn void CsvReader.init(&self, Stream* stream, String separator = ",")
fn void CsvReader.init(&self, InStream* stream, String separator = ",")
{
self.stream = stream;
self.separator = separator;
@@ -15,9 +15,9 @@ fn void CsvReader.init(&self, Stream* stream, String separator = ",")
fn String[]! CsvReader.read_row(self, Allocator* using = mem::heap())
{
@pool()
@pool(using)
{
return self.stream.treadline().split(self.separator, .using = using);
return io::treadline(self.stream).split(self.separator, .using = using);
};
}
@@ -30,13 +30,13 @@ fn void! CsvReader.skip_row(self) @maydiscard
{
@pool()
{
self.stream.readline(mem::temp())!;
(void)io::treadline(self.stream);
};
}
macro CsvReader.@each_row(self, int rows = int.max; @body(String[] row))
{
Stream* stream = self.stream;
InputStream* stream = self.stream;
String sep = self.separator;
while (rows--)
{

View File

@@ -15,7 +15,7 @@ fault JsonParsingError
INVALID_NUMBER,
}
fn Object*! parse(Stream* s, Allocator* using = mem::heap())
fn Object*! parse(InStream* s, Allocator* using = mem::heap())
{
JsonContext context = { .last_string = dstring::new_with_capacity(64, using), .stream = s, .allocator = using };
defer context.last_string.free();
@@ -44,7 +44,7 @@ enum JsonTokenType @local
struct JsonContext @local
{
uint line;
Stream* stream;
InStream* stream;
Allocator* allocator;
JsonTokenType token;
DString last_string;

View File

@@ -2,15 +2,12 @@ module std::io;
struct BitReader
{
Stream reader;
InStream* reader;
uint bits;
uint len;
}
/**
* @require byte_reader.supports_read_byte()
**/
fn void BitReader.init(&self, Stream byte_reader)
fn void BitReader.init(&self, InStream* byte_reader)
{
*self = { .reader = byte_reader };
}
@@ -43,15 +40,12 @@ fn char! BitReader.read_bits(&self, uint nbits)
struct BitWriter
{
Stream writer;
OutStream* writer;
uint bits;
uint len;
}
/**
* @require byte_writer.supports_write_byte()
**/
fn void BitWriter.init(&self, Stream byte_writer)
fn void BitWriter.init(&self, OutStream* byte_writer)
{
*self = { .writer = byte_writer };
}
@@ -63,7 +57,7 @@ fn void! BitWriter.flush(&self)
uint n = (self.len + 7) / 8;
char[4] buffer;
bitorder::write(bits, &buffer, UIntBE);
self.writer.write_all(buffer[:n])!;
io::write_all(self.writer, buffer[:n])!;
self.len = 0;
}
@@ -83,7 +77,7 @@ fn void! BitWriter.write_bits(&self, uint bits, uint nbits)
lbits |= (ulong)(bits >> left) << (64 - (n - left));
char[8] buffer;
bitorder::write(lbits, &buffer, ULongBE);
self.writer.write_all(buffer[:to_write])!;
io::write_all(self.writer, buffer[:to_write])!;
}
self.bits <<= left;
self.bits |= bits & ((1 << left) - 1);

View File

@@ -1,9 +1,8 @@
module std::io;
import libc;
struct File
struct File (InStream, OutStream)
{
inline Stream stream;
CFile file;
}
@@ -22,7 +21,7 @@ fn File! open_path(Path path, String mode)
fn File from_handle(CFile file)
{
return { .stream.fns = &FILESTREAM_INTERFACE, .file = file };
return { .file = file };
}
fn bool is_file(String path)
@@ -49,7 +48,7 @@ fn void! File.reopen(&self, String filename, String mode)
/**
* @require self.file != null
**/
fn usz! File.seek(&self, isz offset, Seek seek_mode = Seek.SET)
fn usz! File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic
{
os::native_fseek(self.file, offset, seek_mode)!;
return os::native_ftell(self.file);
@@ -75,7 +74,7 @@ fn void! File.memopen(File* file, char[] data, String mode)
/**
* @require self.file != null
*/
fn void! File.write_byte(&self, char c)
fn void! File.write_byte(&self, char c) @dynamic
{
if (!libc::fputc(c, self.file)) return IoError.EOF?;
}
@@ -83,7 +82,7 @@ fn void! File.write_byte(&self, char c)
/**
* @param [&inout] self
*/
fn void! File.close(&self) @inline
fn void! File.close(&self) @inline @dynamic
{
if (self.file && libc::fclose(self.file))
{
@@ -117,7 +116,7 @@ fn bool File.eof(&self) @inline
/**
* @param [in] buffer
*/
fn usz! File.read(&self, char[] buffer)
fn usz! File.read(&self, char[] buffer) @dynamic
{
return os::native_fread(self.file, buffer);
}
@@ -126,13 +125,13 @@ fn usz! File.read(&self, char[] buffer)
* @param [out] buffer
* @require self.file `File must be initialized`
*/
fn usz! File.write(&self, char[] buffer)
fn usz! File.write(&self, char[] buffer) @dynamic
{
return os::native_fwrite(self.file, buffer);
}
fn char! File.read_byte(&self)
fn char! File.read_byte(&self) @dynamic
{
int c = libc::fgetc(self.file);
if (c == -1) return IoError.EOF?;
@@ -142,17 +141,7 @@ fn char! File.read_byte(&self)
/**
* @require self.file `File must be initialized`
*/
fn void! File.flush(&self)
fn void! File.flush(&self) @dynamic
{
libc::fflush(self.file);
}
const StreamInterface FILESTREAM_INTERFACE = {
.close_fn = (CloseStreamFn)&File.close,
.seek_fn = (SeekStreamFn)&File.seek,
.read_fn = (ReadStreamFn)&File.read,
.read_byte_fn = (ReadByteStreamFn)&File.read_byte,
.write_fn = (WriteStreamFn)&File.write,
.write_byte_fn = (WriteByteStreamFn)&File.write_byte,
.flush_fn = (FlushStreamFn)&File.flush
};

View File

@@ -45,14 +45,127 @@ fault IoError
}
/**
* @param stream
* @require @is_instream(stream)
**/
macro String! readline(stream = io::stdin(), Allocator* using = mem::heap())
{
bool $is_stream = $typeof(stream).typeid == InStream*.typeid;
$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(using)
{
DString str = dstring::tnew_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(using);
};
}
macro String! treadline(stream = io::stdin()) => readline(stream, mem::temp()) @inline;
/**
* @require @is_outstream(out) "The output must implement OutStream"
*/
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
return printf("%s", x, .out = out);
$endif
$endswitch
}
fn usz! fprintf(OutStream* out, String format, args...)
{
Formatter formatter;
formatter.init(&out_putstream_fn, &out);
return formatter.vprintf(format, args);
}
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 $typeof(out).typeid == OutStream*.typeid:
if (&out.flush) out.flush()!;
$case $defined(out.flush):
out.flush()!;
$endswitch
return len + 1;
}
macro void print(x)
{
(void)print_gen(stdout(), x);
(void)fprint(io::stdout(), x);
}
macro void printn(x = "")
{
(void)printn_gen(stdout(), x);
(void)fprintn(io::stdout(), x);
}
macro void eprint(x)
{
(void)fprint(io::stderr(), x);
}
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
@@ -60,7 +173,6 @@ fn void! out_putchar_fn(void* data @unused, char c) @private
libc::putchar(c);
}
fn usz! printf(String format, args...) @maydiscard
{
Formatter formatter;
@@ -74,9 +186,30 @@ fn usz! printfn(String format, args...) @maydiscard
formatter.init(&out_putchar_fn);
usz len = formatter.vprintf(format, args)!;
putchar('\n');
io::stdout().flush()!;
return len + 1;
}
fn usz! eprintf(String format, args...) @maydiscard
{
Formatter formatter;
OutStream* stream = stderr();
formatter.init(&out_putstream_fn, &stream);
return formatter.vprintf(format, args);
}
fn usz! eprintfn(String format, args...) @maydiscard
{
Formatter formatter;
OutStream* stream = stderr();
formatter.init(&out_putstream_fn, &stream);
usz len = formatter.vprintf(format, args)! + 1;
stderr().write_byte('\n')!;
stderr().flush()!;
return len;
}
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
{
Formatter formatter;
@@ -112,21 +245,21 @@ fn void putchar(char c) @inline
fn File* stdout()
{
static File file;
if (!file.fns) file = file::from_handle(libc::stdout());
if (!file.file) file = file::from_handle(libc::stdout());
return &file;
}
fn File* stderr()
{
static File file;
if (!file.fns) file = file::from_handle(libc::stderr());
if (!file.file) file = file::from_handle(libc::stderr());
return &file;
}
fn File* stdin()
{
static File file;
if (!file.fns) file = file::from_handle(libc::stdin());
if (!file.file) file = file::from_handle(libc::stdin());
return &file;
}

View File

@@ -1,377 +1,166 @@
module std::io;
def CloseStreamFn = fn void!(Stream*);
def FlushStreamFn = fn void!(Stream*);
def SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek);
def LenStreamFn = fn usz(Stream*);
def AvailableStreamFn = fn usz!(Stream*);
def ReadStreamFn = fn usz!(Stream*, char[] bytes);
def ReadFromStreamFn = fn usz!(Stream*, Stream*);
def ReadByteStreamFn = fn char!(Stream*);
def PushbackByteStreamFn = fn void!(Stream*);
def WriteStreamFn = fn usz!(Stream*, char[] bytes);
def WriteToStreamFn = fn usz!(Stream*, Stream* out);
def WriteByteStreamFn = fn void!(Stream*, char c);
def DestroyStreamFn = fn void!(Stream*);
struct StreamInterface
interface InStream
{
CloseStreamFn close_fn;
FlushStreamFn flush_fn;
SeekStreamFn seek_fn;
LenStreamFn len_fn;
AvailableStreamFn available_fn;
ReadStreamFn read_fn;
ReadFromStreamFn read_stream_fn;
ReadByteStreamFn read_byte_fn;
PushbackByteStreamFn pushback_byte_fn;
WriteStreamFn write_fn;
WriteToStreamFn write_stream_fn;
WriteByteStreamFn write_byte_fn;
DestroyStreamFn destroy_fn;
fn void! close() @optional;
fn usz! seek(isz offset, Seek seek) @optional;
fn usz len() @optional;
fn usz! available() @optional;
fn usz! read(char[] buffer);
fn char! read_byte();
fn usz! write_to(OutStream* out) @optional;
fn void! pushback_byte() @optional;
}
struct Stream
interface OutStream
{
StreamInterface* fns;
fn void! destroy() @optional;
fn void! close() @optional;
fn void! flush() @optional;
fn usz! write(char[] bytes);
fn void! write_byte(char c);
fn usz! read_to(InStream* in) @optional;
}
struct StreamWrapper
fn usz! available(InStream* s)
{
inline Stream stream;
void* data;
}
fn bool Stream.supports_flush(&s) @inline => (bool)s.fns.flush_fn;
fn bool Stream.supports_seek(&s) @inline => (bool)s.fns.seek_fn;
fn bool Stream.supports_available(&s) @inline => s.fns.available_fn || s.fns.seek_fn;
fn bool Stream.supports_len(&s) @inline => s.fns.len_fn || s.fns.seek_fn;
fn bool Stream.supports_read(&s) @inline => s.fns.read_fn || s.fns.read_byte_fn;
fn bool Stream.supports_read_from(&s) @inline => (bool)s.fns.read_stream_fn;
fn bool Stream.supports_write_to(&s) @inline => (bool)s.fns.write_stream_fn;
fn bool Stream.supports_pushback_byte(&s) @inline => s.fns.pushback_byte_fn || s.fns.seek_fn;
fn bool Stream.supports_write(&s) @inline => s.fns.write_fn || s.fns.write_byte_fn;
fn bool Stream.supports_read_byte(&s) @inline => (bool)s.fns.read_byte_fn;
fn bool Stream.supports_write_byte(&s) @inline => (bool)s.fns.write_byte_fn;
fn void! Stream.destroy(&self) @inline @maydiscard
{
if (self.fns.destroy_fn) return self.fns.destroy_fn(self);
return self.close();
}
fn void! Stream.close(&self) @inline @maydiscard
{
if (CloseStreamFn func = self.fns.close_fn) return func(self);
}
fn usz! Stream.seek(&self, isz offset, Seek seek) @inline
{
if (SeekStreamFn func = self.fns.seek_fn) return func(self, offset, seek);
return IoError.NOT_SEEKABLE?;
}
fn usz! Stream.available(&self) @inline
{
if (AvailableStreamFn func = self.fns.available_fn) return func(self);
if (SeekStreamFn func = self.fns.seek_fn)
if (&s.available) return s.available();
if (&s.seek)
{
usz curr = func(self, 0, Seek.CURSOR)!;
usz len = func(self, 0, Seek.END)!;
func(self, curr, Seek.SET)!;
usz curr = s.seek(0, Seek.CURSOR)!;
usz len = s.seek(0, Seek.END)!;
s.seek(curr, Seek.SET)!;
return len - curr;
}
return IoError.NOT_SEEKABLE?;
return 0;
}
fn usz! Stream.read_any(&self, any* ref)
macro bool @is_instream(#expr)
{
return self.read_all(ref.ptr[:ref.type.sizeof]);
return $assignable(#expr, InStream*);
}
macro bool @is_outstream(#expr)
{
return $assignable(#expr, OutStream*);
}
/**
* @param ref "the object to write."
* @require ref.ptr != null
* @param [&out] ref
* @require @is_instream(stream)
**/
macro usz! read_any(stream, any* ref)
{
return read_all(stream, ((char*)ref)[:ref.type.sizeof]);
}
/**
* @param [&in] ref "the object to write."
* @require @is_outstream(stream)
* @ensure return == ref.type.sizeof
*/
fn usz! Stream.write_any(&self, any* ref)
macro usz! write_any(stream, any* ref)
{
return self.write_all(ref.ptr[:ref.type.sizeof]);
return write_all(stream, ((char*)ref)[:ref.type.sizeof]);
}
fn usz! Stream.read(&self, char[] buffer)
/**
* @require @is_instream(stream)
*/
macro usz! read_all(stream, char[] buffer)
{
if (ReadStreamFn func = self.fns.read_fn) return func(self, buffer);
if (ReadByteStreamFn func = self.fns.read_byte_fn)
{
usz len = 0;
foreach (&cptr : buffer)
{
char! c = func(self);
if (catch err = c)
{
case IoError.EOF: return len;
default: return err?;
}
*cptr = c;
len++;
}
}
return IoError.UNSUPPORTED_OPERATION?;
}
macro usz! Stream.print(&self, x) @maydiscard
{
return print_gen(self, x);
}
macro usz! print_gen(self, x)
{
var $Type = $typeof(x);
$switch ($Type)
$case String:
return self.write(x);
$case ZString:
return self.write(x.str_view());
$case DString:
return self.write(x.str_view());
$default:
$if $defined((String)x):
return self.write((String)x);
$else
return printf("%s", x);
$endif
$endswitch
}
macro usz! printn_gen(self, x) @maydiscard
{
usz len = print_gen(self, x)!;
self.write_byte('\n')!;
self.flush()!;
return len + 1;
}
macro usz! Stream.printn(&self, x) @maydiscard
{
return printn_gen(self, x);
}
fn usz! Stream.printf(&self, String format, args...) @maydiscard
{
Formatter formatter;
if (WriteByteStreamFn func_byte = self.fns.write_byte_fn)
{
formatter.init((OutputFn)func_byte, self);
}
else if (WriteStreamFn func = self.fns.write_fn)
{
formatter.init((OutputFn)&Stream.write_byte, self);
}
else
{
return IoError.UNSUPPORTED_OPERATION?;
}
return formatter.vprintf(format, args)!;
}
fn usz! Stream.printfn(&self, String format, args...) @maydiscard @inline
{
Formatter formatter;
if (WriteByteStreamFn func_byte = self.fns.write_byte_fn)
{
formatter.init((OutputFn)func_byte, self);
}
else if (WriteStreamFn func = self.fns.write_fn)
{
formatter.init((OutputFn)&Stream.write_byte, self);
}
else
{
return IoError.UNSUPPORTED_OPERATION?;
}
usz len = formatter.vprintf(format, args)!;
self.write_byte('\n')!;
self.flush()!;
return len + 1;
}
fn char! Stream.read_byte(&self) @inline
{
if (ReadByteStreamFn func = self.fns.read_byte_fn) return func(self);
if (ReadStreamFn func = self.fns.read_fn)
{
char[1] buffer;
usz read = func(self, &buffer)!;
if (read != 1) return IoError.EOF?;
return buffer[0];
}
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.read_all(&self, char[] buffer) @inline
{
usz n = self.read(buffer)!;
if (buffer.len == 0) return 0;
usz n = stream.read(buffer)!;
if (n != buffer.len) return IoError.UNEXPECTED_EOF?;
return n;
}
fn usz! Stream.write_all(&self, char[] buffer) @inline
/**
* @require @is_outstream(stream)
*/
macro usz! write_all(stream, char[] buffer)
{
usz n = self.write(buffer)!;
if (buffer.len == 0) return 0;
usz n = stream.write(buffer)!;
if (n != buffer.len) return IoError.INCOMPLETE_WRITE?;
return n;
}
fn String! Stream.treadline(&self) => self.readline(mem::temp()) @inline;
fn String! Stream.readline(&self, Allocator* using = mem::heap())
macro usz! @read_using_read_byte(&s, char[] buffer)
{
if (ReadByteStreamFn func = self.fns.read_byte_fn)
usz len = 0;
foreach (&cptr : buffer)
{
char val = func(self)!;
if (val == '\n') return "";
@pool(using)
{
DString str = dstring::tnew_with_capacity(256);
if (val != '\r') str.append(val);
while (1)
{
char! c = func(self);
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(using);
};
char! c = s.read_byte();
if (catch err = c)
{
case IoError.EOF: return len;
default: return err?;
}
*cptr = c;
len++;
}
if (ReadStreamFn func = self.fns.read_fn)
{
char[1] buff;
if (func(self, &buff)! == 0) return IoError.EOF?;
char val = buff[0];
if (val == '\n') return "";
@pool(using)
{
DString str = dstring::tnew_with_capacity(256);
if (val != '\r') str.append(val);
while (1)
{
usz! read = func(self, &buff);
if (catch err = read)
{
if (err == IoError.EOF) break;
return err?;
}
if (!read) break;
char c = buff[0];
if (c == '\r') continue;
if (c == '\n') break;
str.append_char(c);
}
return str.copy_str(using);
};
}
return IoError.UNSUPPORTED_OPERATION?;
return len;
}
fn usz! Stream.write(&self, char[] bytes) @inline
macro void! @write_byte_using_write(&s, char c)
{
if (WriteStreamFn func = self.fns.write_fn) return func(self, bytes);
if (WriteByteStreamFn func = self.fns.write_byte_fn)
{
foreach (c : bytes) func(self, c)!;
return bytes.len;
}
return IoError.UNSUPPORTED_OPERATION?;
char[1] buff = { c };
(*s).write(&buff)!;
}
fn void! Stream.write_byte(&self, char b) @inline
macro char! @read_byte_using_read(&s)
{
if (WriteByteStreamFn func = self.fns.write_byte_fn) return func(self, b);
return IoError.UNSUPPORTED_OPERATION?;
char[1] buffer;
usz read = (*s).read(&buffer)!;
if (read != 1) return IoError.EOF?;
return buffer[0];
}
fn usz! Stream.write_to(&self, Stream* to) @inline
def ReadByteFn = fn char!();
macro usz! @write_using_write_byte(&s, char[] bytes)
{
if (WriteToStreamFn func = self.fns.write_stream_fn) return func(self, to);
return IoError.UNSUPPORTED_OPERATION?;
foreach (c : bytes) s.write_byte(self, c)!;
return bytes.len;
}
fn usz! Stream.read_from(&self, Stream* from) @inline
macro void! @pushback_using_seek(&s)
{
if (ReadFromStreamFn func = self.fns.read_stream_fn) return func(self, from);
return IoError.UNSUPPORTED_OPERATION?;
s.seek(-1, CURSOR)!;
}
fn void! Stream.flush(&self) @inline @maydiscard
fn usz! copy_to(InStream* in, OutStream* dst, char[] buffer = {})
{
if (FlushStreamFn func = self.fns.flush_fn) return func(self);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.len(&self) @inline
{
if (LenStreamFn func = self.fns.len_fn) return func(self);
if (SeekStreamFn func = self.fns.seek_fn)
{
usz curr = func(self, 0, Seek.CURSOR)!;
usz len = func(self, 0, Seek.END)!;
func(self, curr, Seek.SET)!;
return len;
}
return IoError.NOT_SEEKABLE?;
}
fn void! Stream.pushback_byte(&self) @inline
{
if (PushbackByteStreamFn func = self.fns.pushback_byte_fn) return func(self);
if (SeekStreamFn func = self.fns.seek_fn)
{
func(self, -1, CURSOR)!;
return;
}
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.write_string(&self, String str) @inline => (void)(self.write((char[])str)!);
fn usz! Stream.copy_to(&self, Stream* dst, char[] buffer = {})
{
if (buffer.len) return copy_through_buffer(self, dst, buffer);
if (WriteToStreamFn func = self.fns.write_stream_fn) return func(self, dst);
if (ReadFromStreamFn func = self.fns.read_stream_fn) return func(dst, self);
if (buffer.len) return copy_through_buffer(in, dst, buffer);
if (&in.write_to) return in.write_to(dst);
if (&dst.read_to) return dst.read_to(in);
$switch (env::MEMORY_ENV)
$case NORMAL:
@pool()
{
return copy_through_buffer(self, dst, tmalloc(char, 4096));
return copy_through_buffer(in, dst, tmalloc(char, 4096));
};
$case SMALL:
@pool()
{
return copy_through_buffer(self, dst, tmalloc(char, 1024));
return copy_through_buffer(in, dst, tmalloc(char, 1024));
};
$case TINY:
$case NONE:
return copy_through_buffer(self, dst, &&(char[256]{}));
return copy_through_buffer(in, dst, &&(char[256]{}));
$endswitch
}
macro usz! copy_through_buffer(Stream *self, Stream* dst, char[] buffer) @local
macro usz! copy_through_buffer(InStream *in, OutStream* dst, char[] buffer) @local
{
usz total_copied;
while (true)
{
usz! len = self.read(buffer);
usz! len = in.read(buffer);
if (catch err = len)
{
case IoError.EOF: return total_copied;
@@ -387,9 +176,10 @@ macro usz! copy_through_buffer(Stream *self, Stream* dst, char[] buffer) @local
const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };
/**
* @require @is_instream(stream)
* @require $typeof(x_ptr).kindof == POINTER && $typeof(x_ptr).inner.kindof.is_int()
**/
macro usz! Stream.read_varint(&self, x_ptr)
macro usz! read_varint(stream, x_ptr)
{
var $Type = $typefrom($typeof(x_ptr).inner);
const MAX = MAX_VARS[$Type.sizeof];
@@ -398,7 +188,7 @@ macro usz! Stream.read_varint(&self, x_ptr)
usz n;
for (usz i = 0; i < MAX; i++)
{
char! c = self.read_byte();
char! c = stream.read_byte();
if (catch err = c)
{
case IoError.EOF:
@@ -422,11 +212,11 @@ macro usz! Stream.read_varint(&self, x_ptr)
}
return MathError.OVERFLOW?;
}
/**
* @require @is_outstream(stream)
* @require $typeof(x).kindof.is_int()
**/
macro usz! Stream.write_varint(&self, x)
macro usz! write_varint(stream, x)
{
var $Type = $typeof(x);
const MAX = MAX_VARS[$Type.sizeof];
@@ -439,5 +229,5 @@ macro usz! Stream.write_varint(&self, x)
i++;
}
buffer[i] = (char)x;
return self.write_all(buffer[:i + 1]);
return write_all(stream, buffer[:i + 1]);
}

View File

@@ -1,9 +1,8 @@
module std::io;
struct ReadBuffer
struct ReadBuffer (InStream)
{
inline Stream stream;
Stream* wrapped_stream;
InStream* wrapped_stream;
char[] bytes;
usz read_idx;
usz write_idx;
@@ -15,24 +14,22 @@ struct ReadBuffer
* @require bytes.len > 0
* @require self.bytes.len == 0 "Init may not run on already initialized data"
**/
fn ReadBuffer* ReadBuffer.init(&self, Stream* wrapped_stream, char[] bytes)
fn ReadBuffer* ReadBuffer.init(&self, InStream* wrapped_stream, char[] bytes)
{
*self = { .stream.fns = &READBUFFER_INTERFACE, .wrapped_stream = wrapped_stream, .bytes = bytes };
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
return self;
}
const StreamInterface READBUFFER_INTERFACE = {
.read_fn = (ReadStreamFn)&ReadBuffer.read,
.read_byte_fn = (ReadByteStreamFn)&ReadBuffer.read_byte,
};
fn String ReadBuffer.str_view(&self) @inline
{
return (String)self.bytes[self.read_idx:self.write_idx - self.read_idx];
}
fn usz! ReadBuffer.read(&self, char[] bytes)
fn void! ReadBuffer.close(&self) @dynamic
{
if (&self.wrapped_stream.close) self.wrapped_stream.close()!;
}
fn usz! ReadBuffer.read(&self, char[] bytes) @dynamic
{
if (self.read_idx == self.write_idx)
{
@@ -49,7 +46,7 @@ fn usz! ReadBuffer.read(&self, char[] bytes)
return n;
}
fn char! ReadBuffer.read_byte(&self)
fn char! ReadBuffer.read_byte(&self) @dynamic
{
if (self.read_idx == self.write_idx) self.refill()!;
if (self.read_idx == self.write_idx) return IoError.EOF?;
@@ -64,10 +61,9 @@ fn void! ReadBuffer.refill(&self) @local @inline
self.write_idx = self.wrapped_stream.read(self.bytes)!;
}
struct WriteBuffer
struct WriteBuffer (OutStream)
{
inline Stream stream;
Stream* wrapped_stream;
OutStream* wrapped_stream;
char[] bytes;
usz index;
}
@@ -78,30 +74,29 @@ struct WriteBuffer
* @require bytes.len > 0 "Non-empty buffer required"
* @require self.bytes.len == 0 "Init may not run on already initialized data"
**/
fn WriteBuffer* WriteBuffer.init(&self, Stream* wrapped_stream, char[] bytes)
fn WriteBuffer* WriteBuffer.init(&self, OutStream* wrapped_stream, char[] bytes)
{
*self = { .stream.fns = &WRITEBUFFER_INTERFACE, .wrapped_stream = wrapped_stream, .bytes = bytes };
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
return self;
}
const StreamInterface WRITEBUFFER_INTERFACE = {
.flush_fn = (FlushStreamFn)&WriteBuffer.flush,
.write_fn = (WriteStreamFn)&WriteBuffer.write,
.write_byte_fn = (WriteByteStreamFn)&WriteBuffer.write_byte,
};
fn String WriteBuffer.str_view(&self) @inline
{
return (String)self.bytes[:self.index];
}
fn void! WriteBuffer.flush(&self)
fn void! WriteBuffer.close(&self) @dynamic
{
self.write_pending()!;
if (self.wrapped_stream.supports_flush()) self.wrapped_stream.flush()!;
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
fn usz! WriteBuffer.write(&self, char[] bytes)
fn void! WriteBuffer.flush(&self) @dynamic
{
self.write_pending()!;
if (&self.wrapped_stream.flush) self.wrapped_stream.flush()!;
}
fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic
{
usz n = self.bytes.len - self.index;
if (bytes.len < n)
@@ -123,7 +118,7 @@ fn usz! WriteBuffer.write(&self, char[] bytes)
return bytes.len;
}
fn void! WriteBuffer.write_byte(&self, char c)
fn void! WriteBuffer.write_byte(&self, char c) @dynamic
{
usz n = self.bytes.len - self.index;
if (n == 0) self.write_pending()!;

View File

@@ -1,9 +1,8 @@
module std::io;
import std::math;
struct ByteBuffer
struct ByteBuffer (InStream, OutStream)
{
inline Stream stream;
Allocator* allocator;
usz max_read;
char[] bytes;
@@ -19,7 +18,7 @@ struct ByteBuffer
**/
fn ByteBuffer*! ByteBuffer.init(&self, usz max_read, usz initial_capacity = 16, Allocator* using = mem::heap())
{
*self = { .stream.fns = &BYTEBUFFER_INTERFACE, .allocator = using, .max_read = max_read };
*self = { .allocator = using, .max_read = max_read };
initial_capacity = max(initial_capacity, 16);
self.grow(initial_capacity)!;
return self;
@@ -36,7 +35,7 @@ fn ByteBuffer*! ByteBuffer.tinit(&self, usz max_read, usz initial_capacity = 16)
**/
fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf)
{
*self = { .stream.fns = &BYTEBUFFER_INTERFACE, .max_read = buf.len, .bytes = buf };
*self = { .max_read = buf.len, .bytes = buf };
return self;
}
@@ -46,16 +45,7 @@ fn void ByteBuffer.free(&self)
*self = {};
}
const StreamInterface BYTEBUFFER_INTERFACE = {
.write_fn = (WriteStreamFn)&ByteBuffer.write,
.write_byte_fn = (WriteByteStreamFn)&ByteBuffer.write_byte,
.read_fn = (ReadStreamFn)&ByteBuffer.read,
.read_byte_fn = (ReadByteStreamFn)&ByteBuffer.read_byte,
.pushback_byte_fn = (PushbackByteStreamFn)&ByteBuffer.pushback_byte,
.available_fn = (AvailableStreamFn)&ByteBuffer.available
};
fn usz! ByteBuffer.write(&self, char[] bytes)
fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic
{
usz cap = self.bytes.len - self.write_idx;
if (cap < bytes.len) self.grow(bytes.len)!;
@@ -64,7 +54,7 @@ fn usz! ByteBuffer.write(&self, char[] bytes)
return bytes.len;
}
fn void! ByteBuffer.write_byte(&self, char c)
fn void! ByteBuffer.write_byte(&self, char c) @dynamic
{
usz cap = self.bytes.len - self.write_idx;
if (cap == 0) self.grow(1)!;
@@ -72,7 +62,7 @@ fn void! ByteBuffer.write_byte(&self, char c)
self.write_idx++;
}
fn usz! ByteBuffer.read(&self, char[] bytes)
fn usz! ByteBuffer.read(&self, char[] bytes) @dynamic
{
usz readable = self.write_idx - self.read_idx;
if (readable == 0)
@@ -88,7 +78,7 @@ fn usz! ByteBuffer.read(&self, char[] bytes)
return n;
}
fn char! ByteBuffer.read_byte(&self)
fn char! ByteBuffer.read_byte(&self) @dynamic
{
usz readable = self.write_idx - self.read_idx;
if (readable == 0)
@@ -106,7 +96,7 @@ fn char! ByteBuffer.read_byte(&self)
/*
* Only the last byte of a successful read can be pushed back.
*/
fn void! ByteBuffer.pushback_byte(&self)
fn void! ByteBuffer.pushback_byte(&self) @dynamic
{
if (!self.has_last) return IoError.EOF?;
assert(self.read_idx > 0);
@@ -114,13 +104,14 @@ fn void! ByteBuffer.pushback_byte(&self)
self.has_last = false;
}
fn void! ByteBuffer.seek(&self, isz offset, Seek seek)
fn usz! ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic
{
switch (seek)
{
case SET:
if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?;
self.read_idx = offset;
return offset;
case CURSOR:
if ((offset < 0 && self.read_idx < -offset) ||
(offset > 0 && self.read_idx + offset > self.write_idx)) return IoError.INVALID_POSITION?;
@@ -129,9 +120,10 @@ fn void! ByteBuffer.seek(&self, isz offset, Seek seek)
if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?;
self.read_idx = self.write_idx - offset;
}
return self.read_idx;
}
fn usz! ByteBuffer.available(&self) @inline
fn usz! ByteBuffer.available(&self) @inline @dynamic
{
return self.write_idx - self.read_idx;
}

View File

@@ -1,19 +1,23 @@
module std::io;
struct ByteReader
struct ByteReader (InStream)
{
inline Stream stream;
char[] bytes;
usz index;
}
fn usz ByteReader.len(&self) @dynamic
{
return self.bytes.len;
}
fn ByteReader* ByteReader.init(&self, char[] bytes)
{
*self = { .stream = { &BYTEREADER_INTERFACE }, .bytes = bytes };
*self = { .bytes = bytes };
return self;
}
fn usz! ByteReader.read(&self, char[] bytes)
fn usz! ByteReader.read(&self, char[] bytes) @dynamic
{
if (self.index >= self.bytes.len) return IoError.EOF?;
usz len = min(self.bytes.len - self.index, bytes.len);
@@ -23,19 +27,19 @@ fn usz! ByteReader.read(&self, char[] bytes)
return len;
}
fn char! ByteReader.read_byte(&self)
fn char! ByteReader.read_byte(&self) @dynamic
{
if (self.index >= self.bytes.len) return IoError.EOF?;
return self.bytes[self.index++];
}
fn void! ByteReader.pushback_byte(&self)
fn void! ByteReader.pushback_byte(&self) @dynamic
{
if (!self.index) return IoError.INVALID_PUSHBACK?;
self.index--;
}
fn usz! ByteReader.seek(&self, isz offset, Seek seek)
fn usz! ByteReader.seek(&self, isz offset, Seek seek) @dynamic
{
isz new_index;
switch (seek)
@@ -49,7 +53,7 @@ fn usz! ByteReader.seek(&self, isz offset, Seek seek)
return new_index;
}
fn usz! ByteReader.write_stream(&self, Stream* writer)
fn usz! ByteReader.write_to(&self, OutStream* writer) @dynamic
{
if (self.index >= self.bytes.len) return 0;
usz written = writer.write(self.bytes[self.index..])!;
@@ -58,19 +62,7 @@ fn usz! ByteReader.write_stream(&self, Stream* writer)
return written;
}
fn usz ByteReader.available(&self) @inline
fn usz! ByteReader.available(&self) @inline @dynamic
{
return max(0, self.bytes.len - self.index);
}
const StreamInterface BYTEREADER_INTERFACE = {
.len_fn = fn (Stream* self) => ((ByteReader*)self).bytes.len,
.read_fn = (ReadStreamFn)&ByteReader.read,
.read_byte_fn = (ReadByteStreamFn)&ByteReader.read_byte,
.pushback_byte_fn = (PushbackByteStreamFn)&ByteReader.pushback_byte,
.seek_fn = (SeekStreamFn)&ByteReader.seek,
.write_stream_fn = (WriteToStreamFn)&ByteReader.write_stream,
.available_fn = fn (Stream* self) => ((ByteReader*)self).available(),
};
}

View File

@@ -1,8 +1,7 @@
module std::io;
struct ByteWriter
struct ByteWriter (OutStream)
{
inline Stream stream;
char[] bytes;
usz index;
Allocator* allocator;
@@ -16,13 +15,13 @@ struct ByteWriter
**/
fn ByteWriter* ByteWriter.init(&self, Allocator* using = mem::heap())
{
*self = { .stream.fns = &BYTEWRITER_INTERFACE, .bytes = {}, .allocator = using };
*self = { .bytes = {}, .allocator = using };
return self;
}
fn ByteWriter* ByteWriter.init_buffer(&self, char[] data)
{
*self = { .stream.fns = &BYTEWRITER_INTERFACE, .bytes = data, .allocator = null };
*self = { .bytes = data, .allocator = null };
return self;
}
@@ -35,7 +34,7 @@ fn ByteWriter* ByteWriter.tinit(&self)
return self.init(mem::temp());
}
fn void ByteWriter.destroy(&self)
fn void! ByteWriter.destroy(&self) @dynamic
{
if (!self.allocator) return;
if (void* ptr = self.bytes.ptr) free(ptr, .using = self.allocator);
@@ -57,7 +56,7 @@ fn void! ByteWriter.ensure_capacity(&self, usz len) @inline
self.bytes = new_ptr[:new_capacity];
}
fn usz! ByteWriter.write(&self, char[] bytes)
fn usz! ByteWriter.write(&self, char[] bytes) @dynamic
{
self.ensure_capacity(self.index + bytes.len)!;
mem::copy(&self.bytes[self.index], bytes.ptr, bytes.len);
@@ -65,7 +64,7 @@ fn usz! ByteWriter.write(&self, char[] bytes)
return bytes.len;
}
fn void! ByteWriter.write_byte(&self, char c)
fn void! ByteWriter.write_byte(&self, char c) @dynamic
{
self.ensure_capacity(self.index + 1)!;
self.bytes[self.index++] = c;
@@ -75,10 +74,10 @@ fn void! ByteWriter.write_byte(&self, char c)
* @param [&inout] self
* @param reader
**/
fn usz! ByteWriter.read_from(&self, Stream* reader)
fn usz! ByteWriter.read_from(&self, InStream* reader) @dynamic
{
usz start_index = self.index;
if (reader.supports_available())
if (&reader.available)
{
while (usz available = reader.available()!)
{
@@ -110,11 +109,3 @@ fn usz! ByteWriter.read_from(&self, Stream* reader)
// Otherwise go another round
}
}
const StreamInterface BYTEWRITER_INTERFACE = {
.destroy_fn = fn (s) => ((ByteWriter*)s).destroy(),
.len_fn = fn (s) => ((ByteWriter*)s).bytes.len,
.write_fn = (WriteStreamFn)&ByteWriter.write,
.write_byte_fn = (WriteByteStreamFn)&ByteWriter.write_byte,
.read_stream_fn = (ReadFromStreamFn)&ByteWriter.read_from,
};

View File

@@ -1,38 +0,0 @@
module std::io;
struct DStringStream
{
inline Stream stream;
DString* wrapped_string;
}
fn DStringStream* DStringStream.init(&self, DString* wrapped_string)
{
*self = { .stream.fns = &DSTRINGSTREAM_INTERFACE, .wrapped_string = wrapped_string };
return self;
}
fn usz DStringStream.len(&self) => self.wrapped_string.len();
fn usz! DStringStream.write(&self, char[] bytes)
{
self.wrapped_string.append_chars((String)bytes);
return bytes.len;
}
fn void! DStringStream.write_byte(&self, char c)
{
self.wrapped_string.append_char(c);
}
fn usz! DStringStream.read_from_stream(&self, Stream* reader)
{
return self.wrapped_string.read_from_stream(reader);
}
const StreamInterface DSTRINGSTREAM_INTERFACE = {
.len_fn = (LenStreamFn)&DStringStream.len,
.write_fn = (WriteStreamFn)&DStringStream.write,
.write_byte_fn = (WriteByteStreamFn)&DStringStream.write_byte,
.read_stream_fn = (ReadFromStreamFn)&DStringStream.read_from_stream,
};

View File

@@ -1,9 +1,8 @@
module std::io;
struct LimitReader
struct LimitReader (InStream)
{
inline Stream stream;
Stream* wrapped_stream;
InStream* wrapped_stream;
usz limit;
}
@@ -11,13 +10,19 @@ struct LimitReader
* @param [&inout] wrapped_stream "The stream to read from"
* @param limit "The max limit to read"
**/
fn LimitReader* LimitReader.init(&self, Stream* wrapped_stream, usz limit)
fn LimitReader* LimitReader.init(&self, InStream* wrapped_stream, usz limit)
{
*self = { .stream.fns = &LIMITREADER_INTERFACE, .wrapped_stream = wrapped_stream, .limit = limit };
*self = { .wrapped_stream = wrapped_stream, .limit = limit };
return self;
}
fn usz! LimitReader.read(&self, char[] bytes)
fn void! LimitReader.close(&self) @dynamic
{
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
fn usz! LimitReader.read(&self, char[] bytes) @dynamic
{
if (self.limit == 0) return IoError.EOF?;
usz m = min(bytes.len, self.limit);
@@ -26,20 +31,14 @@ fn usz! LimitReader.read(&self, char[] bytes)
return n;
}
fn char! LimitReader.read_byte(&self)
fn char! LimitReader.read_byte(&self) @dynamic
{
if (self.limit == 0) return IoError.EOF?;
defer try self.limit--;
return self.wrapped_stream.read_byte();
}
fn usz LimitReader.available(&self) @inline
fn usz! LimitReader.available(&self) @inline @dynamic
{
return self.limit;
}
const StreamInterface LIMITREADER_INTERFACE = {
.read_fn = (ReadStreamFn)&LimitReader.read,
.read_byte_fn = (ReadByteStreamFn)&LimitReader.read_byte,
.available_fn = fn(s) => ((LimitReader*)s).available(),
};
}

View File

@@ -1,9 +1,8 @@
module std::io;
struct Scanner
struct Scanner (InStream)
{
inline Stream stream;
Stream* wrapped_stream;
InStream* wrapped_stream;
char[] buf;
usz pattern_idx;
usz read_idx;
@@ -16,23 +15,17 @@ struct Scanner
*
* @param [&in] stream "The stream to read data from."
* @require buffer.len > 0 "Non-empty buffer required."
* @require stream.supports_read() "The stream must support read."
*
**/
fn void Scanner.init(&self, Stream* stream, char[] buffer)
fn void Scanner.init(&self, InStream* stream, char[] buffer)
{
*self = { .stream.fns = &SCANNER_INTERFACE, .wrapped_stream = stream, .buf = buffer };
*self = { .wrapped_stream = stream, .buf = buffer };
}
const StreamInterface SCANNER_INTERFACE = {
.read_fn = (ReadStreamFn)&Scanner.read,
.read_byte_fn = (ReadByteStreamFn)&Scanner.read_byte,
};
/**
* Return and clear any remaining unscanned data.
**/
fn char[] Scanner.flush(&self)
fn char[] Scanner.flush(&self) @dynamic
{
assert(self.read_idx >= self.pattern_idx);
usz n = self.read_idx - self.pattern_idx;
@@ -42,6 +35,11 @@ fn char[] Scanner.flush(&self)
return buf;
}
fn void! Scanner.close(&self) @dynamic
{
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
/**
* Scan the stream for the next split character and return data up to the match.
* @require pattern.len > 0 "Non-empty pattern required."
@@ -102,7 +100,7 @@ macro usz! Scanner.refill(&self, buf) @private
return n;
}
fn usz! Scanner.read(&self, char[] bytes)
fn usz! Scanner.read(&self, char[] bytes) @dynamic
{
usz n;
if (self.pattern_idx < self.read_idx)
@@ -116,11 +114,11 @@ fn usz! Scanner.read(&self, char[] bytes)
return n;
}
fn char! Scanner.read_byte(&self)
fn char! Scanner.read_byte(&self) @dynamic
{
if (self.pattern_idx < self.read_idx)
{
return self.buf[self.pattern_idx++];
}
return self.wrapped_stream.read_byte();
}
}

View File

@@ -2,9 +2,8 @@ module std::net @if(os::SUPPORTS_INET);
import std::io;
import libc;
struct Socket
struct Socket (InStream, OutStream)
{
inline Stream stream;
NativeSocket sock;
Socklen_t ai_addrlen;
// TODO proper way to get the size of sockaddr_storage
@@ -77,7 +76,7 @@ fn ulong! poll(Poll[] polls, Duration timeout)
macro Socket new_socket(fd, ai)
{
Socket sock = { .stream.fns = &SOCKETSTREAM_INTERFACE, .sock = fd, .ai_addrlen = ai.ai_addrlen };
Socket sock = { .sock = fd, .ai_addrlen = ai.ai_addrlen };
assert(sock.ai_addr_storage.len >= ai.ai_addrlen, "storage %d < addrlen %d", sock.ai_addr_storage.len, ai.ai_addrlen);
mem::copy(&sock.ai_addr_storage, (void*)ai.ai_addr, ai.ai_addrlen);
return sock;
@@ -93,12 +92,6 @@ enum SocketOption : char (CInt value)
DONTROUTE (os::SO_DONTROUTE),
}
const StreamInterface SOCKETSTREAM_INTERFACE = {
.read_fn = (ReadStreamFn)&Socket.read,
.write_fn = (WriteStreamFn)&Socket.write,
.close_fn = (CloseStreamFn)&Socket.close,
};
fn bool! Socket.get_broadcast(&self) => self.get_option(BROADCAST);
fn bool! Socket.get_keepalive(&self) => self.get_option(KEEPALIVE);
fn bool! Socket.get_reuseaddr(&self) => self.get_option(REUSEADDR);
@@ -126,21 +119,29 @@ fn bool! Socket.get_option(&self, SocketOption option)
return (bool)flag;
}
fn usz! Socket.read(&self, char[] bytes)
fn usz! Socket.read(&self, char[] bytes) @dynamic
{
isz n = libc::read((Fd)self.sock, bytes.ptr, bytes.len);
if (n < 0) return NetError.READ_FAILED?;
return (usz)n;
}
fn usz! Socket.write(&self, char[] bytes)
fn char! Socket.read_byte(&self) @dynamic => io::@read_byte_using_read(self);
fn usz! Socket.write(&self, char[] bytes) @dynamic
{
isz n = libc::write((Fd)self.sock, bytes.ptr, bytes.len);
if (n < 0) return NetError.WRITE_FAILED?;
return (usz)n;
}
fn void! Socket.close(&self) @inline
fn void! Socket.write_byte(&self, char byte) @dynamic => io::@write_byte_using_write(self, byte);
fn void! Socket.destroy(&self) @dynamic
{
self.close()!;
}
fn void! Socket.close(&self) @inline @dynamic
{
self.sock.close()!;
}