mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Fix conversion if (int x = foo()). Initial stream api. Extended enumset.
This commit is contained in:
@@ -1,83 +1,144 @@
|
||||
// TODO: ensure the type is an enum first.
|
||||
// Copyright (c) 2021 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.
|
||||
|
||||
/**
|
||||
* @require $Type.kindof == TypeKind.ENUM "Only enums maybe be used with an enumset"
|
||||
**/
|
||||
module std::collections::enumset<Enum>;
|
||||
|
||||
$assert(Enum.elements < 64, "Maximum number of elements for an enum used as enum set is 63");
|
||||
|
||||
$switch ($$C_INT_SIZE):
|
||||
$case 64:
|
||||
typedef EnumSetType @private = ulong;
|
||||
$case 32:
|
||||
$if (Enum.elements < 32):
|
||||
typedef EnumSetType @private = uint;
|
||||
$else:
|
||||
$if (Enum.elements > 128):
|
||||
typedef EnumSetType @private = char[(Enum.elements + 7) / 8];
|
||||
const IS_CHAR_ARRAY = true;
|
||||
$elif (Enum.elements > 64):
|
||||
typedef EnumSetType @private = uint128;
|
||||
const IS_CHAR_ARRAY = false;
|
||||
$elif (Enum.elements > 32 || $$C_INT_SIZE > 32):
|
||||
typedef EnumSetType @private = ulong;
|
||||
$endif;
|
||||
$default:
|
||||
$if (Enum.elements < 16):
|
||||
const IS_CHAR_ARRAY = false;
|
||||
$elif (Enum.elements > 16 || $$C_INT_SIZE > 16):
|
||||
typedef EnumSetType @private = uint;
|
||||
const IS_CHAR_ARRAY = false;
|
||||
$elif (Enum.elements > 8 || $$C_INT_SIZE > 8):
|
||||
typedef EnumSetType @private = ushort;
|
||||
$elif (Enum.elements < 31):
|
||||
typedef EnumSetType @private = uint;
|
||||
$else:
|
||||
typedef EnumSetType @private = ulong;
|
||||
$endif;
|
||||
$endswitch;
|
||||
const IS_CHAR_ARRAY = false;
|
||||
$else:
|
||||
typedef EnumSetType @private = char;
|
||||
const IS_CHAR_ARRAY = false;
|
||||
$endif;
|
||||
|
||||
typedef EnumSet = distinct EnumSetType;
|
||||
|
||||
fn void EnumSet.add(EnumSet* this, Enum v)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
(*this)[v / 8] |= (char)(1u << (v % 8));
|
||||
$else:
|
||||
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn void EnumSet.clear(EnumSet* this)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
*this = {};
|
||||
$else:
|
||||
*this = 0;
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn bool EnumSet.remove(EnumSet* this, Enum v)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
if (!this.has(v) @inline) return false;
|
||||
(*this)[v / 8] &= (char)~(1 << (v % 8));
|
||||
return true;
|
||||
$else:
|
||||
EnumSetType old = (EnumSetType)*this;
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v);
|
||||
*this = (EnumSet)new;
|
||||
return old != new;
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn bool EnumSet.has(EnumSet* this, Enum v)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
return (bool)(((*this)[v / 8] << (v % 8)) & 0x01);
|
||||
$else:
|
||||
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn void EnumSet.add_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
foreach (i, c : s) (*this)[i] |= c;
|
||||
$else:
|
||||
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn void EnumSet.retain_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
foreach (i, c : s) (*this)[i] &= c;
|
||||
$else:
|
||||
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn void EnumSet.remove_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
foreach (i, c : s) (*this)[i] &= ~c;
|
||||
$else:
|
||||
*this = (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.and_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
EnumSet copy = *this;
|
||||
copy.retain_all(s);
|
||||
return copy;
|
||||
$else:
|
||||
return (EnumSet)((EnumSetType)*this & (EnumSetType)s);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.or_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
EnumSet copy = *this;
|
||||
copy.add_all(s);
|
||||
return copy;
|
||||
$else:
|
||||
return (EnumSet)((EnumSetType)*this | (EnumSetType)s);
|
||||
$endif;
|
||||
}
|
||||
|
||||
|
||||
fn EnumSet EnumSet.diff_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
EnumSet copy = *this;
|
||||
copy.remove_all(s);
|
||||
return copy;
|
||||
$else:
|
||||
return (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.xor_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if (IS_CHAR_ARRAY):
|
||||
EnumSet copy = *this;
|
||||
foreach (i, c : s) copy[i] ^= c;
|
||||
return copy;
|
||||
$else:
|
||||
return (EnumSet)((EnumSetType)*this ^ (EnumSetType)s);
|
||||
$endif;
|
||||
}
|
||||
@@ -19,19 +19,22 @@ enum Seek
|
||||
fault IoError
|
||||
{
|
||||
FILE_NOT_FOUND,
|
||||
FILE_NOT_SEEKABLE,
|
||||
FILE_NOT_VALID,
|
||||
FILE_INVALID_POSITION,
|
||||
INVALID_POSITION,
|
||||
FILE_OVERFLOW,
|
||||
FILE_IS_PIPE,
|
||||
FILE_EOF,
|
||||
FILE_INCOMPLETE_WRITE,
|
||||
INCOMPLETE_WRITE,
|
||||
FILE_NOT_DIR,
|
||||
NO_PERMISSION,
|
||||
INVALID_PUSHBACK,
|
||||
EOF,
|
||||
NOT_SEEKABLE,
|
||||
NAME_TOO_LONG,
|
||||
INTERRUPTED,
|
||||
GENERAL_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
UNSUPPORTED_OPERATION,
|
||||
}
|
||||
|
||||
fn void putchar(char c) @inline
|
||||
|
||||
@@ -51,19 +51,21 @@ fn void! File.memopen(File* file, char[] data, String mode)
|
||||
/**
|
||||
* @require file.file != null
|
||||
**/
|
||||
fn void! File.seek(File *file, long offset, Seek seekMode = Seek.SET)
|
||||
fn usz! File.seek(File *file, isz offset, Seek seekMode = Seek.SET)
|
||||
{
|
||||
if (libc::fseek(file.file, (SeekIndex)(offset), (int)(seekMode)))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBADF: return IoError.FILE_NOT_SEEKABLE!;
|
||||
case errno::EINVAL: return IoError.FILE_INVALID_POSITION!;
|
||||
case errno::EBADF: return IoError.NOT_SEEKABLE!;
|
||||
case errno::EINVAL: return IoError.INVALID_POSITION!;
|
||||
case errno::EOVERFLOW: return IoError.FILE_OVERFLOW!;
|
||||
case errno::ESPIPE: return IoError.FILE_IS_PIPE!;
|
||||
default: return IoError.UNKNOWN_ERROR!;
|
||||
}
|
||||
}
|
||||
if (seekMode == Seek.SET) return offset;
|
||||
return libc::ftell(file.file);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +95,7 @@ fn void! File.close(File *file) @inline
|
||||
case errno::ENETDOWN:
|
||||
case errno::ENETUNREACH:
|
||||
case errno::ENOSPC:
|
||||
case errno::EIO: return IoError.FILE_INCOMPLETE_WRITE!;
|
||||
case errno::EIO: return IoError.INCOMPLETE_WRITE!;
|
||||
default: return IoError.UNKNOWN_ERROR!;
|
||||
}
|
||||
}
|
||||
@@ -109,24 +111,21 @@ fn bool File.eof(File* file) @inline
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file && file.file
|
||||
* @param [in] buffer
|
||||
*/
|
||||
fn usz File.read(File* file, void* buffer, usz items, usz element_size = 1)
|
||||
fn usz File.read(File* file, char[] buffer)
|
||||
{
|
||||
return libc::fread(buffer, element_size, items, file.file);
|
||||
return libc::fread(buffer.ptr, 1, buffer.len, file.file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @param [&out] buffer
|
||||
* @param items
|
||||
* @param element_size
|
||||
* @require file.file `File must be initialized`
|
||||
* @require element_size > 1
|
||||
*/
|
||||
fn usz File.write(File* file, void* buffer, usz items, usz element_size = 1)
|
||||
fn usz File.write(File* file, char[] buffer)
|
||||
{
|
||||
return libc::fwrite(buffer, element_size, items, file.file);
|
||||
return libc::fwrite(buffer.ptr, 1, buffer.len, file.file);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
205
lib/std/io/io_stream.c3
Normal file
205
lib/std/io/io_stream.c3
Normal file
@@ -0,0 +1,205 @@
|
||||
module std::io;
|
||||
|
||||
typedef CloseStreamFn = fn void!(Stream*);
|
||||
typedef FlushStreamFn = fn void!(Stream*);
|
||||
typedef SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek);
|
||||
typedef LenStreamFn = fn usz(Stream*);
|
||||
typedef AvailableStreamFn = fn usz(Stream*);
|
||||
typedef ReadStreamFn = fn usz!(Stream*, char[] bytes);
|
||||
typedef ReadFromStreamFn = fn usz!(Stream*, Stream*);
|
||||
typedef ReadByteStreamFn = fn char!(Stream*);
|
||||
typedef PushbackByteStreamFn = fn void!(Stream*);
|
||||
typedef WriteStreamFn = fn usz!(Stream*, char[] bytes);
|
||||
typedef WriteToStreamFn = fn usz!(Stream*, Stream* out);
|
||||
typedef WriteByteStreamFn = fn void!(Stream*, char c);
|
||||
typedef DestroyStreamFn = fn void!(Stream*);
|
||||
|
||||
struct StreamInterface
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
struct Stream
|
||||
{
|
||||
StreamInterface *fns;
|
||||
void* data;
|
||||
}
|
||||
|
||||
fn bool Stream.supports_seek(Stream* s) @inline => (bool)s.fns.seek_fn;
|
||||
fn bool Stream.supports_available(Stream* s) @inline => s.fns.available_fn || s.fns.seek_fn;
|
||||
fn bool Stream.supports_len(Stream* s) @inline => s.fns.len_fn || s.fns.seek_fn;
|
||||
fn bool Stream.supports_read(Stream* s) @inline => s.fns.read_fn || s.fns.read_byte_fn;
|
||||
fn bool Stream.supports_read_from(Stream* s) @inline => (bool)s.fns.read_stream_fn;
|
||||
fn bool Stream.supports_write_to(Stream* s) @inline => (bool)s.fns.write_stream_fn;
|
||||
fn bool Stream.supports_pushback_byte(Stream* s) @inline => s.fns.pushback_byte_fn || s.fns.seek_fn;
|
||||
fn bool Stream.supports_write(Stream* s) @inline => s.fns.write_fn || s.fns.write_byte_fn;
|
||||
|
||||
fn void! Stream.destroy(Stream* s) @inline @maydiscard
|
||||
{
|
||||
if (s.fns.destroy_fn) return s.fns.destroy_fn(s);
|
||||
return s.close();
|
||||
}
|
||||
|
||||
fn void! Stream.close(Stream* s) @inline @maydiscard
|
||||
{
|
||||
if (CloseStreamFn func = s.fns.close_fn) return func(s);
|
||||
}
|
||||
|
||||
fn usz! Stream.seek(Stream* s, isz offset, Seek seek) @inline
|
||||
{
|
||||
if (SeekStreamFn func = s.fns.seek_fn) return func(s, offset, seek);
|
||||
return IoError.NOT_SEEKABLE!;
|
||||
}
|
||||
|
||||
fn usz! Stream.available(Stream* s) @inline
|
||||
{
|
||||
if (AvailableStreamFn func = s.fns.available_fn) return func(s);
|
||||
if (SeekStreamFn func = s.fns.seek_fn)
|
||||
{
|
||||
usz curr = func(s, 0, Seek.CURSOR)?;
|
||||
usz len = func(s, 0, Seek.END)?;
|
||||
func(s, curr, Seek.SET)?;
|
||||
return len - curr;
|
||||
}
|
||||
return IoError.NOT_SEEKABLE!;
|
||||
}
|
||||
|
||||
fn usz! Stream.read(Stream* s, char[] buffer)
|
||||
{
|
||||
if (ReadStreamFn func = s.fns.read_fn) return func(s, buffer);
|
||||
if (ReadByteStreamFn func = s.fns.read_byte_fn)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (&cptr : buffer)
|
||||
{
|
||||
char! c = func(s);
|
||||
if (catch err = c)
|
||||
{
|
||||
case IoError.EOF: return len;
|
||||
default: return err!;
|
||||
}
|
||||
*cptr = c;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
}
|
||||
|
||||
fn char! Stream.read_byte(Stream* s) @inline
|
||||
{
|
||||
if (ReadByteStreamFn func = s.fns.read_byte_fn) return func(s);
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
}
|
||||
|
||||
fn usz! Stream.write(Stream* s, char[] bytes) @inline
|
||||
{
|
||||
if (WriteStreamFn func = s.fns.write_fn) return func(s, bytes);
|
||||
if (WriteByteStreamFn func = s.fns.write_byte_fn)
|
||||
{
|
||||
foreach (c : bytes) func(s, c)?;
|
||||
return bytes.len;
|
||||
}
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
}
|
||||
|
||||
fn void! Stream.write_byte(Stream* s, char b) @inline
|
||||
{
|
||||
if (WriteByteStreamFn func = s.fns.write_byte_fn) return func(s, b);
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
}
|
||||
|
||||
fn usz! Stream.write_to(Stream* s, Stream* to) @inline
|
||||
{
|
||||
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, to);
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
}
|
||||
|
||||
fn usz! Stream.read_from(Stream* s, Stream* from) @inline
|
||||
{
|
||||
if (ReadFromStreamFn func = s.fns.read_stream_fn) return func(s, from);
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
}
|
||||
|
||||
fn void! Stream.flush(Stream* s) @inline @maydiscard
|
||||
{
|
||||
if (FlushStreamFn func = s.fns.flush_fn) return func(s);
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
}
|
||||
|
||||
fn usz! Stream.len(Stream* s) @inline
|
||||
{
|
||||
if (LenStreamFn func = s.fns.len_fn) return func(s);
|
||||
if (SeekStreamFn func = s.fns.seek_fn)
|
||||
{
|
||||
usz curr = func(s, 0, Seek.CURSOR)?;
|
||||
usz len = func(s, 0, Seek.END)?;
|
||||
func(s, curr, Seek.SET)?;
|
||||
return len;
|
||||
}
|
||||
return IoError.NOT_SEEKABLE!;
|
||||
}
|
||||
|
||||
fn void! Stream.pushback_byte(Stream* s) @inline
|
||||
{
|
||||
if (PushbackByteStreamFn func = s.fns.pushback_byte_fn) return func(s);
|
||||
if (SeekStreamFn func = s.fns.seek_fn)
|
||||
{
|
||||
func(s, -1, CURSOR)?;
|
||||
return;
|
||||
}
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
}
|
||||
|
||||
fn void! Stream.write_string(Stream* s, String str) @inline => (void)(s.write((char[])str)?);
|
||||
|
||||
fn usz! Stream.copy_to(Stream* s, Stream* dst, char[] buffer = {})
|
||||
{
|
||||
if (buffer.len) return copy_through_buffer(s, dst, buffer);
|
||||
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, dst);
|
||||
if (ReadFromStreamFn func = dst.fns.read_stream_fn) return func(dst, s);
|
||||
$switch (env::MEMORY_ENV):
|
||||
$case NORMAL:
|
||||
@pool()
|
||||
{
|
||||
return copy_through_buffer(s, dst, array::talloc(char, 4096));
|
||||
};
|
||||
$case SMALL:
|
||||
@pool()
|
||||
{
|
||||
return copy_through_buffer(s, dst, array::talloc(char, 1024));
|
||||
};
|
||||
$case TINY:
|
||||
$case NONE:
|
||||
return copy_through_buffer(s, dst, &&(char[256]{}));
|
||||
$endswitch;
|
||||
}
|
||||
|
||||
macro usz! copy_through_buffer(Stream* s, Stream* dst, char[] buffer) @local
|
||||
{
|
||||
usz total_copied;
|
||||
while (true)
|
||||
{
|
||||
usz! len = s.read(buffer);
|
||||
if (catch err = len)
|
||||
{
|
||||
case IoError.EOF: return total_copied;
|
||||
default: return err!;
|
||||
}
|
||||
if (!len) return total_copied;
|
||||
usz written = dst.write(buffer[:len])?;
|
||||
total_copied += len;
|
||||
if (written != len) return IoError.INCOMPLETE_WRITE!;
|
||||
}
|
||||
}
|
||||
80
lib/std/io/stream/bytereader.c3
Normal file
80
lib/std/io/stream/bytereader.c3
Normal file
@@ -0,0 +1,80 @@
|
||||
module std::io;
|
||||
import std::math;
|
||||
|
||||
struct ByteReader
|
||||
{
|
||||
char[] bytes;
|
||||
usz index;
|
||||
}
|
||||
|
||||
fn void ByteReader.init(ByteReader* reader, char[] bytes)
|
||||
{
|
||||
*reader = { .bytes = bytes };
|
||||
}
|
||||
|
||||
fn Stream ByteReader.as_stream(ByteReader* reader)
|
||||
{
|
||||
return { .fns = &bytereader_interface, .data = reader };
|
||||
}
|
||||
|
||||
fn usz! ByteReader.read(ByteReader* reader, char[] bytes)
|
||||
{
|
||||
if (reader.index >= reader.bytes.len) return IoError.EOF!;
|
||||
usz len = math::min(reader.bytes.len - reader.index, bytes.len);
|
||||
if (len == 0) return 0;
|
||||
mem::copy(bytes.ptr, &reader.bytes[reader.index], len);
|
||||
reader.index += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn char! ByteReader.read_byte(ByteReader* reader)
|
||||
{
|
||||
if (reader.index >= reader.bytes.len) return IoError.EOF!;
|
||||
return reader.bytes[reader.index++];
|
||||
}
|
||||
|
||||
fn void! ByteReader.pushback_byte(ByteReader* reader)
|
||||
{
|
||||
if (!reader.index) return IoError.INVALID_PUSHBACK!;
|
||||
reader.index--;
|
||||
}
|
||||
|
||||
fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek)
|
||||
{
|
||||
isz new_index;
|
||||
switch (seek)
|
||||
{
|
||||
case SET: new_index = offset;
|
||||
case CURSOR: new_index = reader.index + offset;
|
||||
case END: new_index = reader.bytes.len + offset;
|
||||
}
|
||||
if (new_index < 0) return IoError.INVALID_POSITION!;
|
||||
reader.index = new_index;
|
||||
return new_index;
|
||||
}
|
||||
|
||||
fn usz! ByteReader.write_stream(ByteReader* reader, Stream* writer)
|
||||
{
|
||||
if (reader.index >= reader.bytes.len) return 0;
|
||||
usz written = writer.write(reader.bytes[reader.index..])?;
|
||||
reader.index += written;
|
||||
assert(reader.index <= reader.bytes.len);
|
||||
return written;
|
||||
}
|
||||
|
||||
fn usz ByteReader.available(ByteReader* reader)
|
||||
{
|
||||
return math::max((isz)0, (isz)reader.bytes.len - reader.index);
|
||||
}
|
||||
|
||||
StreamInterface bytereader_interface = {
|
||||
.len_fn = fn (s) => ((ByteReader*)s.data).bytes.len,
|
||||
.read_fn = fn (s, char[] bytes) => ((ByteReader*)s.data).read(bytes) @inline,
|
||||
.read_byte_fn = fn (s) => ((ByteReader*)s.data).read_byte() @inline,
|
||||
.pushback_byte_fn = fn (s) => ((ByteReader*)s.data).pushback_byte() @inline,
|
||||
.seek_fn = fn (s, offset, seek) => ((ByteReader*)s.data).seek(offset, seek) @inline,
|
||||
.write_stream_fn = fn (s, writer) => ((ByteReader*)s.data).write_stream(writer) @inline,
|
||||
.available_fn = fn (s) => ((ByteReader*)s.data).available() @inline,
|
||||
};
|
||||
|
||||
|
||||
114
lib/std/io/stream/bytewriter.c3
Normal file
114
lib/std/io/stream/bytewriter.c3
Normal file
@@ -0,0 +1,114 @@
|
||||
module std::io;
|
||||
|
||||
struct ByteWriter
|
||||
{
|
||||
char[] bytes;
|
||||
usz index;
|
||||
Allocator* allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] writer
|
||||
* @param [&in] allocator
|
||||
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
|
||||
* @ensure allocator != null, index == 0
|
||||
**/
|
||||
fn void ByteWriter.init(ByteWriter* writer, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
*writer = { .bytes = {}, .allocator = allocator };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] writer
|
||||
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
|
||||
* @ensure allocator != null, index == 0
|
||||
**/
|
||||
fn void ByteWriter.tinit(ByteWriter* writer)
|
||||
{
|
||||
*writer = { .bytes = {}, .allocator = mem::temp_allocator() };
|
||||
}
|
||||
|
||||
fn Stream ByteWriter.as_stream(ByteWriter* writer)
|
||||
{
|
||||
return { .fns = &bytewriter_interface, .data = writer };
|
||||
}
|
||||
|
||||
fn void ByteWriter.destroy(ByteWriter* writer)
|
||||
{
|
||||
if (!writer.allocator) return;
|
||||
if (void* ptr = writer.bytes.ptr) writer.allocator.free(ptr)!!;
|
||||
*writer = { };
|
||||
}
|
||||
|
||||
fn String ByteWriter.as_str(ByteWriter* writer)
|
||||
{
|
||||
return writer.bytes[:writer.index];
|
||||
}
|
||||
|
||||
fn void! ByteWriter.ensure_capacity(ByteWriter* writer, usz len) @inline
|
||||
{
|
||||
if (writer.bytes.len > len) return;
|
||||
if (len < 16) len = 16;
|
||||
usz new_capacity = math::next_power_of_2(len);
|
||||
char* new_ptr = writer.allocator.realloc(writer.bytes.ptr, new_capacity)?;
|
||||
writer.bytes = new_ptr[:new_capacity];
|
||||
}
|
||||
|
||||
fn usz! ByteWriter.write(ByteWriter* writer, char[] bytes)
|
||||
{
|
||||
writer.ensure_capacity(writer.index + bytes.len)?;
|
||||
mem::copy(&writer.bytes[writer.index], bytes.ptr, bytes.len);
|
||||
writer.index += bytes.len;
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
fn void! ByteWriter.write_byte(ByteWriter* writer, char c)
|
||||
{
|
||||
writer.ensure_capacity(writer.index + 1)?;
|
||||
writer.bytes[writer.index++] = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] writer
|
||||
* @param [&inout] reader
|
||||
**/
|
||||
fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader)
|
||||
{
|
||||
if (reader.supports_available())
|
||||
{
|
||||
usz total_read = 0;
|
||||
while (usz available = reader.available()?)
|
||||
{
|
||||
writer.ensure_capacity(writer.index + available)?;
|
||||
usz len = reader.read(writer.bytes[writer.index..])?;
|
||||
total_read += len;
|
||||
writer.index += len;
|
||||
}
|
||||
return total_read;
|
||||
}
|
||||
usz total_read = 0;
|
||||
while (true)
|
||||
{
|
||||
// See how much we can read.
|
||||
usz len_to_read = writer.bytes.len - writer.index;
|
||||
// Less than 16 bytes? Double the capacity
|
||||
if (len_to_read < 16)
|
||||
{
|
||||
writer.ensure_capacity(writer.bytes.len * 2)?;
|
||||
}
|
||||
// Read into the rest of the buffer
|
||||
usz read = reader.read(writer.bytes[writer.index..])?;
|
||||
writer.index += read;
|
||||
// Ok, we reached the end.
|
||||
if (read < len_to_read) return total_read;
|
||||
// Otherwise go another round
|
||||
}
|
||||
}
|
||||
|
||||
StreamInterface bytewriter_interface = {
|
||||
.destroy_fn = fn (s) => ((ByteWriter*)s.data).destroy(),
|
||||
.len_fn = fn (s) => ((ByteWriter*)s.data).bytes.len,
|
||||
.write_fn = fn (s, char[] bytes) => ((ByteWriter*)s.data).write(bytes),
|
||||
.write_byte_fn = fn (s, char c) => ((ByteWriter*)s.data).write_byte(c),
|
||||
.read_stream_fn = fn (s, reader) => ((ByteWriter*)s.data).read_from(reader),
|
||||
};
|
||||
18
lib/std/io/stream/filestream.c3
Normal file
18
lib/std/io/stream/filestream.c3
Normal file
@@ -0,0 +1,18 @@
|
||||
module std::io;
|
||||
|
||||
fn Stream File.as_stream(File* file)
|
||||
{
|
||||
return { .fns = &filestream_interface, .data = file };
|
||||
}
|
||||
|
||||
StreamInterface filestream_interface = {
|
||||
.close_fn = fn (s) => ((File*)s.data).close(),
|
||||
.seek_fn = fn (s, offset, seek) => ((File*)s.data).seek(offset, seek) @inline,
|
||||
.read_fn = fn (s, char[] bytes) => ((File*)s.data).read(bytes) @inline,
|
||||
.write_fn = fn (s, char[] bytes) => ((File*)s.data).write(bytes) @inline,
|
||||
.write_byte_fn = fn (s, char c) => ((File*)s.data).putc(c) @inline,
|
||||
.read_byte_fn = fn (s) => ((File*)s.data).getc() @inline,
|
||||
.flush_fn = fn (s) => ((File*)s.data).flush() @inline,
|
||||
};
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ static void llvm_emit_decl_expr_list(GenContext *context, BEValue *be_value, Exp
|
||||
if (type->type_kind != TYPE_BOOL)
|
||||
{
|
||||
CastKind cast = cast_to_bool_kind(type);
|
||||
llvm_emit_cast(context, cast, last, be_value, type, type_bool);
|
||||
llvm_emit_cast(context, cast, last, be_value, type_bool, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,6 +324,7 @@ RESOLVE_LAMBDA:;
|
||||
}
|
||||
FOREACH_END();
|
||||
if (found_lambda) goto RESOLVE_LAMBDA;
|
||||
halt_on_error();
|
||||
|
||||
if (active_target.strip_unused && !active_target.testing)
|
||||
{
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.4.80"
|
||||
#define COMPILER_VERSION "0.4.81"
|
||||
25
test/unit/stdlib/io/bytestream.c3
Normal file
25
test/unit/stdlib/io/bytestream.c3
Normal file
@@ -0,0 +1,25 @@
|
||||
module std::io @test;
|
||||
|
||||
fn void! bytestream()
|
||||
{
|
||||
ByteReader r;
|
||||
r.init("abc");
|
||||
Stream s = r.as_stream();
|
||||
assert(s.len()? == 3);
|
||||
char[5] buffer;
|
||||
assert('a' == s.read_byte()?);
|
||||
s.pushback_byte()?;
|
||||
usz len = s.read(&buffer)?;
|
||||
assert(buffer[:len] == "abc");
|
||||
ByteWriter w;
|
||||
w.init();
|
||||
Stream ws = w.as_stream();
|
||||
ws.write("helloworld")?;
|
||||
assert(w.as_str() == "helloworld");
|
||||
s.seek(0, SET)?;
|
||||
ws.read_from(&s)?;
|
||||
s.seek(1, SET)?;
|
||||
s.write_to(&ws)?;
|
||||
assert(w.as_str() == "helloworldabcbc");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user