Merge branch 'master' into parser-experiments

This commit is contained in:
gingerBill
2020-12-06 00:49:48 +00:00
committed by GitHub
86 changed files with 14317 additions and 5037 deletions
+68
View File
@@ -0,0 +1,68 @@
package bufio
import "core:io"
// Read_Writer stores pointers to a Reader and a Writer
Read_Writer :: struct {
r: ^Reader,
w: ^Writer,
}
read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
rw.r, rw.w = r, w;
}
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
s.stream_data = rw;
s.stream_vtable = _read_writer_vtable;
return;
}
@(private)
_read_writer_vtable := &io.Stream_VTable{
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Read_Writer)(s.stream_data).r;
return reader_read(b, p);
},
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
b := (^Read_Writer)(s.stream_data).r;
return reader_read_byte(b);
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
b := (^Read_Writer)(s.stream_data).r;
return reader_unread_byte(b);
},
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
b := (^Read_Writer)(s.stream_data).r;
return reader_read_rune(b);
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
b := (^Read_Writer)(s.stream_data).r;
return reader_unread_rune(b);
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
b := (^Read_Writer)(s.stream_data).r;
return reader_write_to(b, w);
},
impl_flush = proc(s: io.Stream) -> io.Error {
b := (^Read_Writer)(s.stream_data).w;
return writer_flush(b);
},
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Read_Writer)(s.stream_data).w;
return writer_write(b, p);
},
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
b := (^Read_Writer)(s.stream_data).w;
return writer_write_byte(b, c);
},
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
b := (^Read_Writer)(s.stream_data).w;
return writer_write_rune(b, r);
},
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
b := (^Read_Writer)(s.stream_data).w;
return writer_read_from(b, r);
},
};
+474
View File
@@ -0,0 +1,474 @@
package bufio
import "core:io"
import "core:mem"
import "core:unicode/utf8"
import "core:bytes"
// Reader is a buffered wrapper for an io.Reader
Reader :: struct {
buf: []byte,
buf_allocator: mem.Allocator,
rd: io.Reader, // reader
r, w: int, // read and write positions for buf
err: io.Error,
last_byte: int, // last byte read, invalid is -1
last_rune_size: int, // size of last rune read, invalid is -1
}
DEFAULT_BUF_SIZE :: 4096;
@(private)
MIN_READ_BUFFER_SIZE :: 16;
@(private)
MAX_CONSECUTIVE_EMPTY_READS :: 128;
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
size := size;
size = max(size, MIN_READ_BUFFER_SIZE);
reader_reset(b, rd);
b.buf_allocator = allocator;
b.buf = make([]byte, size, allocator);
}
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
reader_reset(b, rd);
b.buf_allocator = {};
b.buf = buf;
}
// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
reader_destroy :: proc(b: ^Reader) {
delete(b.buf, b.buf_allocator);
b^ = {};
}
reader_size :: proc(b: ^Reader) -> int {
return len(b.buf);
}
reader_reset :: proc(b: ^Reader, r: io.Reader) {
b.rd = r;
b.r, b.w = 0, 0;
b.err = nil;
b.last_byte = -1;
b.last_rune_size = -1;
}
@(private)
_reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
if b.r > 0 {
copy(b.buf, b.buf[b.r:b.w]);
b.w -= b.r;
b.r = 0;
}
if b.w >= len(b.buf) {
return .Buffer_Full;
}
// read new data, and try a limited number of times
for i := MAX_CONSECUTIVE_EMPTY_READS; i > 0; i -= 1 {
n, err := io.read(b.rd, b.buf[b.w:]);
if n < 0 {
return .Negative_Read;
}
b.w += n;
if err != nil {
b.err = err;
return nil;
}
if n > 0 {
return nil;
}
}
b.err = .No_Progress;
return nil;
}
@(private)
_reader_consume_err :: proc(b: ^Reader) -> io.Error {
err := b.err;
b.err = nil;
return err;
}
// reader_peek returns the next n bytes without advancing the reader
// The bytes stop being valid on the next read call
// If reader_peek returns fewer than n bytes, it also return an error
// explaining why the read is short
// The error will be .Buffer_Full if n is larger than the internal buffer size
reader_peek :: proc(b: ^Reader, n: int) -> (data: []byte, err: io.Error) {
n := n;
if n < 0 {
return nil, .Negative_Count;
}
b.last_byte = -1;
b.last_rune_size = -1;
for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
return nil, fill_err;
}
}
if n > len(b.buf) {
return b.buf[b.r : b.w], .Buffer_Full;
}
if available := b.w - b.r; available < n {
n = available;
err = _reader_consume_err(b);
if err == nil {
err = .Buffer_Full;
}
}
return b.buf[b.r : b.r+n], err;
}
// reader_buffered returns the number of bytes that can be read from the current buffer
reader_buffered :: proc(b: ^Reader) -> int {
return b.w - b.r;
}
// reader_discard skips the next n bytes, and returns the number of bytes that were discarded
reader_discard :: proc(b: ^Reader, n: int) -> (discarded: int, err: io.Error) {
if n < 0 {
return 0, .Negative_Count;
}
if n == 0 {
return;
}
remaining := n;
for {
skip := reader_buffered(b);
if skip == 0 {
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
return 0, fill_err;
}
skip = reader_buffered(b);
}
skip = min(skip, remaining);
b.r += skip;
remaining -= skip;
if remaining == 0 {
return n, nil;
}
if b.err != nil {
return n - remaining, _reader_consume_err(b);
}
}
return;
}
// reader_read reads data into p
// The bytes are taken from at most one read on the underlying Reader, which means n may be less than len(p)
reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
n = len(p);
if n == 0 {
if reader_buffered(b) > 0 {
return 0, nil;
}
return 0, _reader_consume_err(b);
}
if b.r == b.w {
if b.err != nil {
return 0, _reader_consume_err(b);
}
if len(p) >= len(b.buf) {
n, b.err = io.read(b.rd, p);
if n < 0 {
return 0, .Negative_Read;
}
if n > 0 {
b.last_byte = int(p[n-1]);
b.last_rune_size = -1;
}
return n, _reader_consume_err(b);
}
b.r, b.w = 0, 0;
n, b.err = io.read(b.rd, b.buf);
if n < 0 {
return 0, .Negative_Read;
}
if n == 0 {
return 0, _reader_consume_err(b);
}
b.w += n;
}
n = copy(p, b.buf[b.r:b.w]);
b.r += n;
b.last_byte = int(b.buf[b.r-1]);
b.last_rune_size = -1;
return n, nil;
}
// reader_read_byte reads and returns a single byte
// If no byte is available, it return an error
reader_read_byte :: proc(b: ^Reader) -> (byte, io.Error) {
b.last_rune_size = -1;
for b.r == b.w {
if b.err != nil {
return 0, _reader_consume_err(b);
}
if err := _reader_read_new_chunk(b); err != nil {
return 0, err;
}
}
c := b.buf[b.r];
b.r += 1;
b.last_byte = int(c);
return c, nil;
}
// reader_unread_byte unreads the last byte. Only the most recently read byte can be unread
reader_unread_byte :: proc(b: ^Reader) -> io.Error {
if b.last_byte < 0 || b.r == 0 && b.w > 0 {
return .Invalid_Unread;
}
if b.r > 0 {
b.r -= 1;
} else {
// b.r == 0 && b.w == 0
b.w = 1;
}
b.buf[b.r] = byte(b.last_byte);
b.last_byte = -1;
b.last_rune_size = -1;
return nil;
}
// reader_read_rune reads a single UTF-8 encoded unicode character
// and returns the rune and its size in bytes
// If the encoded rune is invalid, it consumes one byte and returns utf8.RUNE_ERROR (U+FFFD) with a size of 1
reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) {
for b.r+utf8.UTF_MAX > b.w &&
!utf8.full_rune(b.buf[b.r:b.w]) &&
b.err == nil &&
b.w-b.w < len(b.buf) {
if err = _reader_read_new_chunk(b); err != nil {
return;
}
}
b.last_rune_size = -1;
if b.r == b.w {
err = _reader_consume_err(b);
return;
}
r, size = rune(b.buf[b.r]), 1;
if r >= utf8.RUNE_SELF {
r, size = utf8.decode_rune(b.buf[b.r : b.w]);
}
b.r += size;
b.last_byte = int(b.buf[b.r-1]);
b.last_rune_size = size;
return;
}
// reader_unread_rune unreads the last rune. Only the most recently read rune can be unread
reader_unread_rune :: proc(b: ^Reader) -> io.Error {
if b.last_rune_size < 0 || b.r < b.last_rune_size {
return .Invalid_Unread;
}
b.r -= b.last_rune_size;
b.last_byte = -1;
b.last_rune_size = -1;
return nil;
}
reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
n, err := io.write(w, b.buf[b.r:b.w]);
if n < 0 {
return 0, .Negative_Write;
}
b.r += n;
return i64(n), err;
}
n, err = write_buf(b, w);
if err != nil {
return;
}
m: i64;
if nr, cerr := io.to_writer_to(b.rd); cerr == nil {
m, err = io.write_to(nr, w);
n += m;
return n, err;
}
if nw, cerr := io.to_reader_from(w); cerr == nil {
m, err = io.read_from(nw, b.rd);
n += m;
return n, err;
}
if b.w-b.r < len(b.buf) {
if err = _reader_read_new_chunk(b); err != nil {
return;
}
}
for b.r < b.w {
m, err = write_buf(b, w);
n += m;
if err != nil {
return;
}
if err = _reader_read_new_chunk(b); err != nil {
return;
}
}
if b.err == .EOF {
b.err = nil;
}
err = _reader_consume_err(b);
return;
}
// reader_to_stream converts a Reader into an io.Stream
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
s.stream_data = b;
s.stream_vtable = _reader_vtable;
return;
}
@(private)
_reader_vtable := &io.Stream_VTable{
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Reader)(s.stream_data);
reader_destroy(b);
return nil;
},
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Reader)(s.stream_data);
return reader_read(b, p);
},
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
b := (^Reader)(s.stream_data);
return reader_read_byte(b);
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
b := (^Reader)(s.stream_data);
return reader_unread_byte(b);
},
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
b := (^Reader)(s.stream_data);
return reader_read_rune(b);
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
b := (^Reader)(s.stream_data);
return reader_unread_rune(b);
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
b := (^Reader)(s.stream_data);
return reader_write_to(b, w);
},
};
//
// Utility procedures
//
// reader_read_slice reads until the first occurrence of delim from the reader
// It returns a slice pointing at the bytes in the buffer
// The bytes stop being valid at the next read
// If reader_read_slice encounters an error before finding a delimiter
// reader_read_slice fails with error .Buffer_Full if the buffer fills without a delim
// Because the data returned from reader_read_slice will be overwritten on the
// next IO operation, reader_read_bytes or reader_read_string is usually preferred
//
// reader_read_slice returns err != nil if and only if line does not end in delim
//
reader_read_slice :: proc(b: ^Reader, delim: byte) -> (line: []byte, err: io.Error) {
s := 0;
for {
if i := bytes.index_byte(b.buf[b.r+s : b.w], delim); i >= 0 {
i += s;
line = b.buf[b.r:][:i+1];
b.r += i + 1;
break;
}
if b.err != nil {
line = b.buf[b.r : b.w];
b.r = b.w;
err = _reader_consume_err(b);
break;
}
if reader_buffered(b) >= len(b.buf) {
b.r = b.w;
line = b.buf;
err = .Buffer_Full;
break;
}
s = b.w - b.r;
if err = _reader_read_new_chunk(b); err != nil {
break;
}
}
if i := len(line)-1; i >= 0 {
b.last_byte = int(line[i]);
b.last_rune_size = -1;
}
return;
}
// reader_read_bytes reads until the first occurrence of delim from the Reader
// It returns an allocated slice containing the data up to and including the delimiter
reader_read_bytes :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (buf: []byte, err: io.Error) {
full: [dynamic]byte;
full.allocator = allocator;
frag: []byte;
for {
e: io.Error;
frag, e = reader_read_slice(b, delim);
if e == nil {
break;
}
if e != .Buffer_Full {
err = e;
break;
}
append(&full, ..frag);
}
append(&full, ..frag);
return full[:], err;
}
// reader_read_string reads until the first occurrence of delim from the Reader
// It returns an allocated string containing the data up to and including the delimiter
reader_read_string :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (string, io.Error) {
buf, err := reader_read_bytes(b, delim, allocator);
return string(buf), err;
}
+255
View File
@@ -0,0 +1,255 @@
package bufio
import "core:io"
import "core:mem"
import "core:unicode/utf8"
// import "core:bytes"
// Writer is a buffered wrapper for an io.Writer
Writer :: struct {
buf: []byte,
buf_allocator: mem.Allocator,
wr: io.Writer,
n: int,
err: io.Error,
}
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
size := size;
size = max(size, MIN_READ_BUFFER_SIZE);
writer_reset(b, wr);
b.buf_allocator = allocator;
b.buf = make([]byte, size, allocator);
}
writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
writer_reset(b, wr);
b.buf_allocator = {};
b.buf = buf;
}
// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
writer_destroy :: proc(b: ^Writer) {
delete(b.buf, b.buf_allocator);
b^ = {};
}
// writer_size returns the size of underlying buffer in bytes
writer_size :: proc(b: ^Writer) -> int {
return len(b.buf);
}
writer_reset :: proc(b: ^Writer, w: io.Writer) {
b.wr = w;
b.n = 0;
b.err = nil;
}
// writer_flush writes any buffered data into the underlying io.Writer
writer_flush :: proc(b: ^Writer) -> io.Error {
if b.err != nil {
return b.err;
}
if b.n == 0 {
return nil;
}
n, err := io.write(b.wr, b.buf[0:b.n]);
if n < b.n && err == nil {
err = .Short_Write;
}
if err != nil {
if n > 0 && n < b.n {
copy(b.buf[:b.n-n], b.buf[n : b.n]);
}
b.n -= n;
b.err = err;
return err;
}
b.n = 0;
return nil;
}
// writer_available returns how many bytes are unused in the buffer
writer_available :: proc(b: ^Writer) -> int {
return len(b.buf) - b.n;
}
// writer_buffered returns the number of bytes that have been writted into the current buffer
writer_buffered :: proc(b: ^Writer) -> int {
return b.n;
}
// writer_write writes the contents of p into the buffer
// It returns the number of bytes written
// If n < len(p), it will return an error explaining why the write is short
writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) {
p := p;
for len(p) > writer_available(b) && b.err == nil {
m: int;
if writer_buffered(b) == 0 {
m, b.err = io.write(b.wr, p);
} else {
m = copy(b.buf[b.n:], p);
b.n += m;
writer_flush(b);
}
n += m;
p = p[m:];
}
if b.err != nil {
return n, b.err;
}
m := copy(b.buf[b.n:], p);
b.n += m;
m += n;
return m, nil;
}
// writer_write_byte writes a single byte
writer_write_byte :: proc(b: ^Writer, c: byte) -> io.Error {
if b.err != nil {
return b.err;
}
if writer_available(b) <= 0 && writer_flush(b) != nil {
return b.err;
}
b.buf[b.n] = c;
b.n += 1;
return nil;
}
// writer_write_rune writes a single unicode code point, and returns the number of bytes written with any error
writer_write_rune :: proc(b: ^Writer, r: rune) -> (size: int, err: io.Error) {
if r < utf8.RUNE_SELF {
err = writer_write_byte(b, byte(r));
size = 0 if err != nil else 1;
return;
}
if b.err != nil {
return 0, b.err;
}
buf: [4]u8;
n := writer_available(b);
if n < utf8.UTF_MAX {
writer_flush(b);
if b.err != nil {
return 0, b.err;
}
n = writer_available(b);
if n < utf8.UTF_MAX {
// this only happens if the buffer is very small
w: int;
buf, w = utf8.encode_rune(r);
return writer_write(b, buf[:w]);
}
}
buf, size = utf8.encode_rune(r);
copy(b.buf[b.n:], buf[:size]);
b.n += size;
return;
}
// writer_write writes a string into the buffer
// It returns the number of bytes written
// If n < len(p), it will return an error explaining why the write is short
writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) {
return writer_write(b, transmute([]byte)s);
}
// writer_read_from is to support io.Reader_From types
// If the underlying writer supports the io,read_from, and b has no buffered data yet,
// this procedure calls the underlying read_from implementation without buffering
writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
if b.err != nil {
return 0, b.err;
}
if writer_buffered(b) == 0 {
if w, cerr := io.to_reader_from(b.wr); cerr != nil {
n, err = io.read_from(w, r);
b.err = err;
return;
}
}
for {
if writer_available(b) == 0 {
if ferr := writer_flush(b); ferr != nil {
return n, ferr;
}
}
m: int;
nr := 0;
for nr < MAX_CONSECUTIVE_EMPTY_READS {
m, err = io.read(r, b.buf[b.n:]);
if m != 0 || err != nil {
break;
}
nr += 1;
}
if nr == MAX_CONSECUTIVE_EMPTY_READS {
return n, .No_Progress;
}
b.n += m;
n += i64(m);
if err != nil {
break;
}
}
if err == .EOF {
if writer_available(b) == 0 {
err = writer_flush(b);
} else {
err = nil;
}
}
return;
}
// writer_to_stream converts a Writer into an io.Stream
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
s.stream_data = b;
s.stream_vtable = _writer_vtable;
return;
}
@(private)
_writer_vtable := &io.Stream_VTable{
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Writer)(s.stream_data);
writer_destroy(b);
return nil;
},
impl_flush = proc(s: io.Stream) -> io.Error {
b := (^Writer)(s.stream_data);
return writer_flush(b);
},
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Writer)(s.stream_data);
return writer_write(b, p);
},
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
b := (^Writer)(s.stream_data);
return writer_write_byte(b, c);
},
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
b := (^Writer)(s.stream_data);
return writer_write_rune(b, r);
},
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
b := (^Writer)(s.stream_data);
return writer_read_from(b, r);
},
};
+335
View File
@@ -0,0 +1,335 @@
package bytes
import "core:io"
import "core:unicode/utf8"
MIN_READ :: 512;
@(private)
SMALL_BUFFER_SIZE :: 64;
Buffer :: struct {
buf: [dynamic]byte,
off: int,
last_read: Read_Op,
}
@(private)
Read_Op :: enum i8 {
Read = -1,
Invalid = 0,
Read_Rune1 = 1,
Read_Rune2 = 2,
Read_Rune3 = 3,
Read_Rune4 = 4,
}
buffer_init :: proc(b: ^Buffer, buf: []byte) {
resize(&b.buf, len(buf));
copy(b.buf[:], buf);
}
buffer_init_string :: proc(b: ^Buffer, s: string) {
resize(&b.buf, len(s));
copy(b.buf[:], s);
}
buffer_destroy :: proc(b: ^Buffer) {
delete(b.buf);
buffer_reset(b);
}
buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
return b.buf[b.off:];
}
buffer_to_string :: proc(b: ^Buffer) -> string {
if b == nil {
return "<nil>";
}
return string(b.buf[b.off:]);
}
buffer_is_empty :: proc(b: ^Buffer) -> bool {
return len(b.buf) <= b.off;
}
buffer_length :: proc(b: ^Buffer) -> int {
return len(b.buf) - b.off;
}
buffer_capacity :: proc(b: ^Buffer) -> int {
return cap(b.buf);
}
buffer_reset :: proc(b: ^Buffer) {
clear(&b.buf);
b.off = 0;
b.last_read = .Invalid;
}
buffer_truncate :: proc(b: ^Buffer, n: int) {
if n == 0 {
buffer_reset(b);
return;
}
b.last_read = .Invalid;
if n < 0 || n > buffer_length(b) {
panic("bytes.truncate: truncation out of range");
}
resize(&b.buf, b.off+n);
}
@(private)
_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
if l := len(b.buf); n <= cap(b.buf)-l {
resize(&b.buf, l+n);
return l, true;
}
return 0, false;
}
@(private)
_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
m := buffer_length(b);
if m == 0 && b.off != 0 {
buffer_reset(b);
}
if i, ok := _buffer_try_grow(b, n); ok {
return i;
}
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
return 0;
}
c := cap(b.buf);
if n <= c/2 - m {
copy(b.buf[:], b.buf[b.off:]);
} else if c > max(int) - c - n {
panic("bytes.Buffer: too large");
} else {
resize(&b.buf, 2*c + n);
copy(b.buf[:], b.buf[b.off:]);
}
b.off = 0;
resize(&b.buf, m+n);
return m;
}
buffer_grow :: proc(b: ^Buffer, n: int) {
if n < 0 {
panic("bytes.buffer_grow: negative count");
}
m := _buffer_grow(b, n);
resize(&b.buf, m);
}
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, len(p));
if !ok {
m = _buffer_grow(b, len(p));
}
return copy(b.buf[m:], p), nil;
}
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, len(s));
if !ok {
m = _buffer_grow(b, len(s));
}
return copy(b.buf[m:], s), nil;
}
buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, 1);
if !ok {
m = _buffer_grow(b, 1);
}
b.buf[m] = c;
return nil;
}
buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
if r < utf8.RUNE_SELF {
buffer_write_byte(b, byte(r));
return 1, nil;
}
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
if !ok {
m = _buffer_grow(b, utf8.UTF_MAX);
}
res: [4]byte;
res, n = utf8.encode_rune(r);
copy(b.buf[m:][:utf8.UTF_MAX], res[:n]);
resize(&b.buf, m+n);
return;
}
buffer_next :: proc(b: ^Buffer, n: int) -> []byte {
n := n;
b.last_read = .Invalid;
m := buffer_length(b);
if n > m {
n = m;
}
data := b.buf[b.off : b.off + n];
b.off += n;
if n > 0 {
b.last_read = .Read;
}
return data;
}
buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
if buffer_is_empty(b) {
buffer_reset(b);
if len(p) == 0 {
return 0, nil;
}
return 0, .EOF;
}
n = copy(p, b.buf[b.off:]);
b.off += n;
if n > 0 {
b.last_read = .Read;
}
return;
}
buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
if buffer_is_empty(b) {
buffer_reset(b);
return 0, .EOF;
}
c := b.buf[b.off];
b.off += 1;
b.last_read = .Read;
return c, nil;
}
buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) {
if buffer_is_empty(b) {
buffer_reset(b);
return 0, 0, .EOF;
}
c := b.buf[b.off];
if c < utf8.RUNE_SELF {
b.off += 1;
b.last_read = .Read_Rune1;
return rune(c), 1, nil;
}
r, size = utf8.decode_rune(b.buf[b.off:]);
b.off += size;
b.last_read = Read_Op(i8(size));
return;
}
buffer_unread_byte :: proc(b: ^Buffer) -> io.Error {
if b.last_read == .Invalid {
return .Invalid_Unread;
}
b.last_read = .Invalid;
if b.off > 0 {
b.off -= 1;
}
return nil;
}
buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
if b.last_read <= .Invalid {
return .Invalid_Unread;
}
if b.off >= int(b.last_read) {
b.off -= int(i8(b.last_read));
}
b.last_read = .Invalid;
return nil;
}
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
i := index_byte(b.buf[b.off:], delim);
end := b.off + i + 1;
if i < 0 {
end = len(b.buf);
err = .EOF;
}
line = b.buf[b.off:end];
b.off = end;
b.last_read = .Read;
return;
}
buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) {
slice: []byte;
slice, err = buffer_read_bytes(b, delim);
return string(slice), err;
}
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
s.stream_data = b;
s.stream_vtable = _buffer_vtable;
return;
}
@(private)
_buffer_vtable := &io.Stream_VTable{
impl_size = proc(s: io.Stream) -> i64 {
b := (^Buffer)(s.stream_data);
return i64(buffer_capacity(b));
},
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read(b, p);
},
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read_byte(b);
},
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read_rune(b);
},
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_write(b, p);
},
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
b := (^Buffer)(s.stream_data);
return buffer_write_byte(b, c);
},
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_write_rune(b, r);
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
b := (^Buffer)(s.stream_data);
return buffer_unread_byte(b);
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
b := (^Buffer)(s.stream_data);
return buffer_unread_rune(b);
},
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Buffer)(s.stream_data);
buffer_destroy(b);
return nil;
},
// TODO(bill): write_to and read_from
// impl_write_to = nil,
// impl_read_from = nil,
};
+177
View File
@@ -0,0 +1,177 @@
package bytes
import "core:io"
import "core:unicode/utf8"
Reader :: struct {
s: []byte, // read-only buffer
i: i64, // current reading index
prev_rune: int, // previous reading index of rune or < 0
}
reader_init :: proc(r: ^Reader, s: []byte) {
r.s = s;
r.i = 0;
r.prev_rune = -1;
}
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
s.stream_data = r;
s.stream_vtable = _reader_vtable;
return;
}
reader_length :: proc(r: ^Reader) -> int {
if r.i >= i64(len(r.s)) {
return 0;
}
return int(i64(len(r.s)) - r.i);
}
reader_size :: proc(r: ^Reader) -> i64 {
return i64(len(r.s));
}
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
return 0, .EOF;
}
r.prev_rune = -1;
n = copy(p, r.s[r.i:]);
r.i += i64(n);
return;
}
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
if off < 0 {
return 0, .Invalid_Offset;
}
if off >= i64(len(r.s)) {
return 0, .EOF;
}
n = copy(p, r.s[off:]);
if n < len(p) {
err = .EOF;
}
return;
}
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.prev_rune = -1;
if r.i >= i64(len(r.s)) {
return 0, .EOF;
}
b := r.s[r.i];
r.i += 1;
return b, nil;
}
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread;
}
r.prev_rune = -1;
r.i -= 1;
return nil;
}
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
r.prev_rune = -1;
return 0, 0, .EOF;
}
r.prev_rune = int(r.i);
if c := r.s[r.i]; c < utf8.RUNE_SELF {
r.i += 1;
return rune(c), 1, nil;
}
ch, size = utf8.decode_rune(r.s[r.i:]);
r.i += i64(size);
return;
}
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread;
}
if r.prev_rune < 0 {
return .Invalid_Unread;
}
r.i = i64(r.prev_rune);
r.prev_rune = -1;
return nil;
}
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r.prev_rune = -1;
abs: i64;
switch whence {
case .Start:
abs = offset;
case .Current:
abs = r.i + offset;
case .End:
abs = i64(len(r.s)) + offset;
case:
return 0, .Invalid_Whence;
}
if abs < 0 {
return 0, .Invalid_Offset;
}
r.i = abs;
return abs, nil;
}
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
r.prev_rune = -1;
if r.i >= i64(len(r.s)) {
return 0, nil;
}
s := r.s[r.i:];
m: int;
m, err = io.write(w, s);
if m > len(s) {
panic("bytes.Reader.write_to: invalid io.write_string count");
}
r.i += i64(m);
n = i64(m);
if m != len(s) && err == nil {
err = .Short_Write;
}
return;
}
@(private)
_reader_vtable := &io.Stream_VTable{
impl_size = proc(s: io.Stream) -> i64 {
r := (^Reader)(s.stream_data);
return reader_size(r);
},
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_read(r, p);
},
impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_read_at(r, p, off);
},
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
r := (^Reader)(s.stream_data);
return reader_read_byte(r);
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
r := (^Reader)(s.stream_data);
return reader_unread_byte(r);
},
impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_read_rune(r);
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
r := (^Reader)(s.stream_data);
return reader_unread_rune(r);
},
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r := (^Reader)(s.stream_data);
return reader_seek(r, offset, whence);
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_write_to(r, w);
},
};
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,3 +1,3 @@
package dynlib
Library :: opaque rawptr;
Library :: #opaque rawptr;
+7 -8
View File
@@ -14,7 +14,8 @@ Marshal_Error :: enum {
}
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
b := strings.make_builder(allocator);
b: strings.Builder;
strings.init_builder(&b, allocator);
err := marshal_arg(&b, v);
@@ -129,7 +130,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
case b32: val = bool(b);
case b64: val = bool(b);
}
write_string(b, val ? "true" : "false");
write_string_builder(b, val ? "true" : "false");
case Type_Info_Any:
return .Unsupported_Type;
@@ -208,14 +209,12 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
if i > 0 { write_string(b, ", "); }
data := uintptr(entries.data) + uintptr(i*entry_size);
header := cast(^Map_Entry_Header)data;
marshal_arg(b, any{rawptr(&header.key.key.val), info.key.id});
key := rawptr(data + entry_type.offsets[2]);
value := rawptr(data + entry_type.offsets[3]);
marshal_arg(b, any{key, info.key.id});
write_string(b, ": ");
value := data + entry_type.offsets[2];
marshal_arg(b, any{rawptr(value), info.value.id});
marshal_arg(b, any{value, info.value.id});
}
}
write_byte(b, '}');
+311 -275
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -1,4 +1,5 @@
// This is purely for documentation
//+ignore
package intrinsics
// Types
@@ -114,7 +115,7 @@ type_is_ordered_numeric :: proc($T: typeid) -> bool ---
type_is_indexable :: proc($T: typeid) -> bool ---
type_is_sliceable :: proc($T: typeid) -> bool ---
type_is_comparable :: proc($T: typeid) -> bool ---
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
type_is_dereferenceable :: proc($T: typeid) -> bool ---
type_is_valid_map_key :: proc($T: typeid) -> bool ---
@@ -152,3 +153,6 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V --
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) ---
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) ---
+200
View File
@@ -0,0 +1,200 @@
package io
Conversion_Error :: enum {
None,
Missing_Procedure,
Fallback_Possible,
}
to_reader :: proc(s: Stream) -> (r: Reader, err: Conversion_Error) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil {
err = .Missing_Procedure;
}
return;
}
to_writer :: proc(s: Stream) -> (w: Writer, err: Conversion_Error) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil {
err = .Missing_Procedure;
}
return;
}
to_closer :: proc(s: Stream) -> (c: Closer, err: Conversion_Error) {
c.stream = s;
if s.stream_vtable == nil || s.impl_close == nil {
err = .Missing_Procedure;
}
return;
}
to_flusher :: proc(s: Stream) -> (f: Flusher, err: Conversion_Error) {
f.stream = s;
if s.stream_vtable == nil || s.impl_flush == nil {
err = .Missing_Procedure;
}
return;
}
to_seeker :: proc(s: Stream) -> (seeker: Seeker, err: Conversion_Error) {
seeker.stream = s;
if s.stream_vtable == nil || s.impl_seek == nil {
err = .Missing_Procedure;
}
return;
}
to_read_writer :: proc(s: Stream) -> (r: Read_Writer, err: Conversion_Error) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil {
err = .Missing_Procedure;
}
return;
}
to_read_closer :: proc(s: Stream) -> (r: Read_Closer, err: Conversion_Error) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil {
err = .Missing_Procedure;
}
return;
}
to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, err: Conversion_Error) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil {
err = .Missing_Procedure;
}
return;
}
to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, err: Conversion_Error) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil {
err = .Missing_Procedure;
}
return;
}
to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, err: Conversion_Error) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil {
err = .Missing_Procedure;
}
return;
}
to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, err: Conversion_Error) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil {
err = .Missing_Procedure;
}
return;
}
to_reader_at :: proc(s: Stream) -> (r: Reader_At, err: Conversion_Error) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read_at == nil {
err = .Missing_Procedure;
}
return;
}
to_writer_at :: proc(s: Stream) -> (w: Writer_At, err: Conversion_Error) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write_at == nil {
err = .Missing_Procedure;
}
return;
}
to_reader_from :: proc(s: Stream) -> (r: Reader_From, err: Conversion_Error) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read_from == nil {
err = .Missing_Procedure;
}
return;
}
to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write_to == nil {
err = .Missing_Procedure;
}
return;
}
to_write_closer :: proc(s: Stream) -> (w: Write_Closer, err: Conversion_Error) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil {
err = .Missing_Procedure;
}
return;
}
to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, err: Conversion_Error) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil {
err = .Missing_Procedure;
}
return;
}
to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, err: Conversion_Error) {
b.stream = s;
if s.stream_vtable == nil || s.impl_read_byte == nil {
err = .Missing_Procedure;
if s.stream_vtable != nil && s.impl_read != nil {
err = .Fallback_Possible;
}
}
return;
}
to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, err: Conversion_Error) {
b.stream = s;
if s.stream_vtable != nil {
if s.impl_unread_byte == nil {
err = .Missing_Procedure;
return;
}
if s.impl_read_byte != nil {
err = .None;
} else if s.impl_read != nil {
err = .Fallback_Possible;
} else {
err = .Missing_Procedure;
}
}
return;
}
to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, err: Conversion_Error) {
b.stream = s;
if s.stream_vtable == nil || s.impl_write_byte == nil {
err = .Missing_Procedure;
if s.stream_vtable != nil && s.impl_write != nil {
err = .Fallback_Possible;
}
}
return;
}
to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, err: Conversion_Error) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read_rune == nil {
err = .Missing_Procedure;
if s.stream_vtable != nil && s.impl_read != nil {
err = .Fallback_Possible;
}
}
return;
}
to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, err: Conversion_Error) {
r.stream = s;
if s.stream_vtable != nil {
if s.impl_unread_rune == nil {
err = .Missing_Procedure;
return;
}
if s.impl_read_rune != nil {
err = .None;
} else if s.impl_read != nil {
err = .Fallback_Possible;
} else {
err = .Missing_Procedure;
}
} else {
err = .Missing_Procedure;
}
return;
}
+504
View File
@@ -0,0 +1,504 @@
package io
import "intrinsics"
import "core:runtime"
import "core:unicode/utf8"
Seek_From :: enum {
Start = 0, // seek relative to the origin of the file
Current = 1, // seek relative to the current offset
End = 2, // seek relative to the end
}
Error :: enum i32 {
// No Error
None = 0,
// EOF is the error returned by `read` when no more input is available
EOF,
// Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
Unexpected_EOF,
// Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
Short_Write,
// Short_Buffer means that a read required a longer buffer than was provided
Short_Buffer,
// No_Progress is returned by some implementations of `io.Reader` when many calls
// to `read` have failed to return any data or error.
// This is usually a signed of a broken `io.Reader` implementation
No_Progress,
Invalid_Whence,
Invalid_Offset,
Invalid_Unread,
Negative_Read,
Negative_Write,
Negative_Count,
Buffer_Full,
// Empty is returned when a procedure has not been implemented for an io.Stream
Empty = -1,
}
Close_Proc :: proc(using s: Stream) -> Error;
Flush_Proc :: proc(using s: Stream) -> Error;
Seek_Proc :: proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error);
Size_Proc :: proc(using s: Stream) -> i64;
Read_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
Read_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
Read_From_Proc :: proc(using s: Stream, r: Reader) -> (n: i64, err: Error);
Read_Byte_Proc :: proc(using s: Stream) -> (byte, Error);
Read_Rune_Proc :: proc(using s: Stream) -> (ch: rune, size: int, err: Error);
Unread_Byte_Proc :: proc(using s: Stream) -> Error;
Unread_Rune_Proc :: proc(using s: Stream) -> Error;
Write_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
Write_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
Write_To_Proc :: proc(using s: Stream, w: Writer) -> (n: i64, err: Error);
Write_Byte_Proc :: proc(using s: Stream, c: byte) -> Error;
Write_Rune_Proc :: proc(using s: Stream, r: rune) -> (size: int, err: Error);
Destroy_Proc :: proc(using s: Stream) -> Error;
Stream :: struct {
using stream_vtable: ^Stream_VTable,
stream_data: rawptr,
}
Stream_VTable :: struct {
impl_close: Close_Proc,
impl_flush: Flush_Proc,
impl_seek: Seek_Proc,
impl_size: Size_Proc,
impl_read: Read_Proc,
impl_read_at: Read_At_Proc,
impl_read_byte: Read_Byte_Proc,
impl_read_rune: Read_Rune_Proc,
impl_write_to: Write_To_Proc,
impl_write: Write_Proc,
impl_write_at: Write_At_Proc,
impl_write_byte: Write_Byte_Proc,
impl_write_rune: Write_Rune_Proc,
impl_read_from: Read_From_Proc,
impl_unread_byte: Unread_Byte_Proc,
impl_unread_rune: Unread_Rune_Proc,
impl_destroy: Destroy_Proc,
}
Reader :: struct {using stream: Stream};
Writer :: struct {using stream: Stream};
Closer :: struct {using stream: Stream};
Flusher :: struct {using stream: Stream};
Seeker :: struct {using stream: Stream};
Read_Writer :: struct {using stream: Stream};
Read_Closer :: struct {using stream: Stream};
Read_Write_Closer :: struct {using stream: Stream};
Read_Write_Seeker :: struct {using stream: Stream};
Write_Closer :: struct {using stream: Stream};
Write_Seeker :: struct {using stream: Stream};
Write_Flusher :: struct {using stream: Stream};
Write_Flush_Closer :: struct {using stream: Stream};
Reader_At :: struct {using stream: Stream};
Writer_At :: struct {using stream: Stream};
Reader_From :: struct {using stream: Stream};
Writer_To :: struct {using stream: Stream};
Byte_Reader :: struct {using stream: Stream};
Byte_Scanner :: struct {using stream: Stream};
Byte_Writer :: struct {using stream: Stream};
Rune_Reader :: struct {using stream: Stream};
Rune_Scanner :: struct {using stream: Stream};
destroy :: proc(s: Stream) -> Error {
close_err := close({s});
if s.stream_vtable != nil && s.impl_destroy != nil {
return s->impl_destroy();
}
if close_err != .None {
return close_err;
}
return .Empty;
}
read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) {
if s.stream_vtable != nil && s.impl_read != nil {
return s->impl_read(p);
}
return 0, .Empty;
}
write :: proc(s: Writer, p: []byte) -> (n: int, err: Error) {
if s.stream_vtable != nil && s.impl_write != nil {
return s->impl_write(p);
}
return 0, .Empty;
}
seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
if s.stream_vtable != nil && s.impl_seek != nil {
return s->impl_seek(offset, whence);
}
return 0, .Empty;
}
close :: proc(s: Closer) -> Error {
if s.stream_vtable != nil && s.impl_close != nil {
return s->impl_close();
}
// Instead of .Empty, .None is fine in this case
return .None;
}
flush :: proc(s: Flusher) -> Error {
if s.stream_vtable != nil && s.impl_flush != nil {
return s->impl_flush();
}
// Instead of .Empty, .None is fine in this case
return .None;
}
size :: proc(s: Stream) -> i64 {
if s.stream_vtable == nil {
return 0;
}
if s.impl_size != nil {
return s->impl_size();
}
if s.impl_seek == nil {
return 0;
}
curr, end: i64;
err: Error;
if curr, err = s->impl_seek(0, .Current); err != nil {
return 0;
}
if end, err = s->impl_seek(0, .End); err != nil {
return 0;
}
if _, err = s->impl_seek(curr, .Start); err != nil {
return 0;
}
return end;
}
read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) {
if r.stream_vtable == nil {
return 0, .Empty;
}
if r.impl_read_at != nil {
return r->impl_read_at(p, offset);
}
if r.impl_seek == nil || r.impl_read == nil {
return 0, .Empty;
}
current_offset: i64;
current_offset, err = r->impl_seek(offset, .Current);
if err != nil {
return 0, err;
}
n, err = r->impl_read(p);
if err != nil {
return;
}
_, err = r->impl_seek(current_offset, .Start);
return;
}
write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) {
if w.stream_vtable == nil {
return 0, .Empty;
}
if w.impl_write_at != nil {
return w->impl_write_at(p, offset);
}
if w.impl_seek == nil || w.impl_write == nil {
return 0, .Empty;
}
current_offset: i64;
current_offset, err = w->impl_seek(offset, .Current);
if err != nil {
return 0, err;
}
defer w->impl_seek(current_offset, .Start);
return w->impl_write(p);
}
write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) {
if r.stream_vtable == nil || w.stream_vtable == nil {
return 0, .Empty;
}
if r.impl_write_to != nil {
return r->impl_write_to(w);
}
return 0, .Empty;
}
read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
if r.stream_vtable == nil || w.stream_vtable == nil {
return 0, .Empty;
}
if r.impl_read_from != nil {
return w->impl_read_from(r);
}
return 0, .Empty;
}
read_byte :: proc(r: Byte_Reader) -> (byte, Error) {
if r.stream_vtable == nil {
return 0, .Empty;
}
if r.impl_read_byte != nil {
return r->impl_read_byte();
}
if r.impl_read == nil {
return 0, .Empty;
}
b: [1]byte;
_, err := r->impl_read(b[:]);
return b[0], err;
}
write_byte :: proc{
write_byte_to_byte_writer,
write_byte_to_writer,
};
write_byte_to_byte_writer :: proc(w: Byte_Writer, c: byte) -> Error {
return _write_byte(auto_cast w, c);
}
write_byte_to_writer :: proc(w: Writer, c: byte) -> Error {
return _write_byte(auto_cast w, c);
}
@(private)
_write_byte :: proc(w: Byte_Writer, c: byte) -> Error {
if w.stream_vtable == nil {
return .Empty;
}
if w.impl_write_byte != nil {
return w->impl_write_byte(c);
}
if w.impl_write == nil {
return .Empty;
}
b := [1]byte{c};
_, err := w->impl_write(b[:]);
return err;
}
read_rune :: proc(br: Rune_Reader) -> (ch: rune, size: int, err: Error) {
if br.stream_vtable == nil {
return 0, 0, .Empty;
}
if br.impl_read_rune != nil {
return br->impl_read_rune();
}
if br.impl_read == nil {
return 0, 0, .Empty;
}
b: [utf8.UTF_MAX]byte;
_, err = br->impl_read(b[:1]);
s0 := b[0];
ch = rune(s0);
size = 1;
if err != nil {
return;
}
if ch < utf8.RUNE_SELF {
return;
}
x := utf8.accept_sizes[s0];
if x >= 0xf0 {
mask := rune(x) << 31 >> 31;
ch = ch &~ mask | utf8.RUNE_ERROR&mask;
return;
}
sz := int(x&7);
n: int;
n, err = br->impl_read(b[1:sz]);
if err != nil || n+1 < sz {
ch = utf8.RUNE_ERROR;
return;
}
ch, size = utf8.decode_rune(b[:sz]);
return;
}
unread_byte :: proc(s: Byte_Scanner) -> Error {
if s.stream_vtable != nil && s.impl_unread_byte != nil {
return s->impl_unread_byte();
}
return .Empty;
}
unread_rune :: proc(s: Rune_Scanner) -> Error {
if s.stream_vtable != nil && s.impl_unread_rune != nil {
return s->impl_unread_rune();
}
return .Empty;
}
write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) {
return write(s, transmute([]byte)str);
}
write_rune :: proc(s: Writer, r: rune) -> (size: int, err: Error) {
if s.stream_vtable != nil && s.impl_write_rune != nil {
return s->impl_write_rune(r);
}
if r < utf8.RUNE_SELF {
err = write_byte(s, byte(r));
if err == nil {
size = 1;
}
return;
}
buf, w := utf8.encode_rune(r);
return write(s, buf[:w]);
}
read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
return read_at_least(r, buf, len(buf));
}
read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min {
return 0, .Short_Buffer;
}
for n < min && err == nil {
nn: int;
nn, err = read(r, buf[n:]);
n += n;
}
if n >= min {
err = nil;
} else if n > 0 && err == .EOF {
err = .Unexpected_EOF;
}
return;
}
// copy copies from src to dst till either EOF is reached on src or an error occurs
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) {
return _copy_buffer(dst, src, nil);
}
// copy_buffer is the same as copy except that it stages through the provided buffer (if one is required)
// rather than allocating a temporary one on the stack through `intrinsics.alloca`
// If buf is `nil`, it is allocate through `intrinsics.alloca`; otherwise if it has zero length, it will panic
copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
if buf != nil && len(buf) == 0 {
panic("empty buffer in io.copy_buffer");
}
return _copy_buffer(dst, src, buf);
}
// copy_n copies n bytes (or till an error) from src to dst.
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
// On return, written == n IFF err == nil
copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) {
nsrc := inline_limited_reader(&Limited_Reader{}, src, n);
written, err = copy(dst, nsrc);
if written == n {
return n, nil;
}
if written < n && err == nil {
// src stopped early and must have been an EOF
err = .EOF;
}
return;
}
@(private)
_copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
if dst.stream_vtable == nil || src.stream_vtable == nil {
return 0, .Empty;
}
if src.impl_write_to != nil {
return src->impl_write_to(dst);
}
if src.impl_read_from != nil {
return dst->impl_read_from(src);
}
buf := buf;
if buf == nil {
DEFAULT_SIZE :: 4 * 1024;
size := DEFAULT_SIZE;
if src.stream_vtable == _limited_reader_vtable {
l := (^Limited_Reader)(src.stream_data);
if i64(size) > l.n {
if l.n < 1 {
size = 1;
} else {
size = int(l.n);
}
}
}
// NOTE(bill): alloca is fine here
buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size};
}
for {
nr, er := read(src, buf);
if nr > 0 {
nw, ew := write(dst, buf[0:nr]);
if nw > 0 {
written += i64(nw);
}
if ew != nil {
err = ew;
break;
}
if nr != nw {
err = .Short_Write;
break;
}
}
if er != nil {
if er != .EOF {
err = er;
}
break;
}
}
return;
}
+113
View File
@@ -0,0 +1,113 @@
package io
import "core:runtime"
@(private)
Multi_Reader :: struct {
readers: [dynamic]Reader,
}
@(private)
_multi_reader_vtable := &Stream_VTable{
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
mr := (^Multi_Reader)(s.stream_data);
for len(mr.readers) > 0 {
r := mr.readers[0];
n, err = read(r, p);
if err == .EOF {
ordered_remove(&mr.readers, 0);
}
if n > 0 || err != .EOF {
if err == .EOF && len(mr.readers) > 0 {
// Don't return EOF yet, more readers remain
err = nil;
}
return;
}
}
return 0, .EOF;
},
impl_destroy = proc(s: Stream) -> Error {
mr := (^Multi_Reader)(s.stream_data);
context.allocator = mr.readers.allocator;
delete(mr.readers);
free(mr);
return .None;
},
};
multi_reader :: proc(readers: ..Reader, allocator := context.allocator) -> (r: Reader) {
context.allocator = allocator;
mr := new(Multi_Reader);
all_readers := make([dynamic]Reader, 0, len(readers));
for w in readers {
if w.stream_vtable == _multi_reader_vtable {
other := (^Multi_Reader)(w.stream_data);
append(&all_readers, ..other.readers[:]);
} else {
append(&all_readers, w);
}
}
mr.readers = all_readers;
r.stream_vtable = _multi_reader_vtable;
r.stream_data = mr;
return;
}
@(private)
Multi_Writer :: struct {
writers: []Writer,
allocator: runtime.Allocator,
}
@(private)
_multi_writer_vtable := &Stream_VTable{
impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
mw := (^Multi_Writer)(s.stream_data);
for w in mw.writers {
n, err = write(w, p);
if err != nil {
return;
}
if n != len(p) {
err = .Short_Write;
return;
}
}
return len(p), nil;
},
impl_destroy = proc(s: Stream) -> Error {
mw := (^Multi_Writer)(s.stream_data);
context.allocator = mw.allocator;
delete(mw.writers);
free(mw);
return .None;
},
};
multi_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (out: Writer) {
context.allocator = allocator;
mw := new(Multi_Writer);
mw.allocator = allocator;
all_writers := make([dynamic]Writer, 0, len(writers));
for w in writers {
if w.stream_vtable == _multi_writer_vtable {
other := (^Multi_Writer)(w.stream_data);
append(&all_writers, ..other.writers);
} else {
append(&all_writers, w);
}
}
mw.writers = all_writers[:];
out.stream_vtable = _multi_writer_vtable;
out.stream_data = mw;
return;
}
+192
View File
@@ -0,0 +1,192 @@
package io
import "core:runtime"
import "core:strconv"
write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) {
buf: [32]byte;
s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil);
return write_string(w, s);
}
write_i64 :: proc(w: Writer, i: i64, base: int = 10) -> (n: int, err: Error) {
buf: [32]byte;
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil);
return write_string(w, s);
}
write_uint :: proc(w: Writer, i: uint, base: int = 10) -> (n: int, err: Error) {
return write_u64(w, u64(i), base);
}
write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) {
return write_i64(w, i64(i), base);
}
@(private)
Tee_Reader :: struct {
r: Reader,
w: Writer,
allocator: runtime.Allocator,
}
@(private)
_tee_reader_vtable := &Stream_VTable{
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
t := (^Tee_Reader)(s.stream_data);
n, err = read(t.r, p);
if n > 0 {
if wn, werr := write(t.w, p[:n]); werr != nil {
return wn, werr;
}
}
return;
},
impl_destroy = proc(s: Stream) -> Error {
t := (^Tee_Reader)(s.stream_data);
allocator := t.allocator;
free(t, allocator);
return .None;
},
};
// tee_reader returns a Reader that writes to 'w' what it reads from 'r'
// All reads from 'r' performed through it are matched with a corresponding write to 'w'
// There is no internal buffering done
// The write must complete before th read completes
// Any error encountered whilst writing is reported as a 'read' error
// tee_reader must call io.destroy when done with
tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> (out: Reader) {
t := new(Tee_Reader, allocator);
t.r, t.w = r, w;
t.allocator = allocator;
out.stream_data = t;
out.stream_vtable = _tee_reader_vtable;
return;
}
// A Limited_Reader reads from r but limits the amount of data returned to just n bytes.
// Each call to read updates n to reflect the new amount remaining.
// read returns EOF when n <= 0 or when the underlying r returns EOF.
Limited_Reader :: struct {
r: Reader, // underlying reader
n: i64, // max_bytes
}
@(private)
_limited_reader_vtable := &Stream_VTable{
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
l := (^Limited_Reader)(s.stream_data);
if l.n <= 0 {
return 0, .EOF;
}
p := p;
if i64(len(p)) > l.n {
p = p[0:l.n];
}
n, err = read(l.r, p);
l.n -= i64(n);
return;
},
};
new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader {
l := new(Limited_Reader);
l.r = r;
l.n = n;
return l;
}
limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) {
r.stream_vtable = _limited_reader_vtable;
r.stream_data = l;
return;
}
@(private="package")
inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
l.r = r;
l.n = n;
return limited_reader_to_reader(l);
}
// Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At
Section_Reader :: struct {
r: Reader_At,
base: i64,
off: i64,
limit: i64,
}
init_section_reader :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) {
s.r = r;
s.off = off;
s.limit = off + n;
return;
}
section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) {
out.stream_data = s;
out.stream_vtable = _section_reader_vtable;
return;
}
@(private)
_section_reader_vtable := &Stream_VTable{
impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) {
s := (^Section_Reader)(stream.stream_data);
if s.off >= s.limit {
return 0, .EOF;
}
p := p;
if max := s.limit - s.off; i64(len(p)) > max {
p = p[0:max];
}
n, err = read_at(s.r, p, s.off);
s.off += i64(n);
return;
},
impl_read_at = proc(stream: Stream, p: []byte, off: i64) -> (n: int, err: Error) {
s := (^Section_Reader)(stream.stream_data);
p, off := p, off;
if off < 0 || off >= s.limit - s.base {
return 0, .EOF;
}
off += s.base;
if max := s.limit - off; i64(len(p)) > max {
p = p[0:max];
n, err = read_at(s.r, p, off);
if err == nil {
err = .EOF;
}
return;
}
return read_at(s.r, p, off);
},
impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
s := (^Section_Reader)(stream.stream_data);
offset := offset;
switch whence {
case:
return 0, .Invalid_Whence;
case .Start:
offset += s.base;
case .Current:
offset += s.off;
case .End:
offset += s.limit;
}
if offset < s.base {
return 0, .Invalid_Offset;
}
s.off = offset;
n = offset - s.base;
return;
},
impl_size = proc(stream: Stream) -> i64 {
s := (^Section_Reader)(stream.stream_data);
return s.limit - s.base;
},
};
+24
View File
@@ -593,6 +593,30 @@ is_inf :: proc{is_inf_f32, is_inf_f64};
inf_f32 :: proc(sign: int) -> f32 {
return f32(inf_f64(sign));
}
inf_f64 :: proc(sign: int) -> f64 {
v: u64;
if sign >= 0 {
v = 0x7ff00000_00000000;
} else {
v = 0xfff00000_00000000;
}
return transmute(f64)v;
}
nan_f32 :: proc() -> f32 {
return f32(nan_f64());
}
nan_f64 :: proc() -> f64 {
v: u64 = 0x7ff80000_00000001;
return transmute(f64)v;
}
is_power_of_two :: proc(x: int) -> bool {
return x > 0 && (x & (x-1)) == 0;
}
+11 -4
View File
@@ -146,6 +146,7 @@ Paren_Expr :: struct {
Selector_Expr :: struct {
using node: Expr,
expr: ^Expr,
op: tokenizer.Token,
field: ^Ident,
}
@@ -154,6 +155,13 @@ Implicit_Selector_Expr :: struct {
field: ^Ident,
}
Selector_Call_Expr :: struct {
using node: Expr,
expr: ^Expr,
call: ^Call_Expr,
modified_call: bool,
}
Index_Expr :: struct {
using node: Expr,
expr: ^Expr,
@@ -206,9 +214,9 @@ Ternary_Expr :: struct {
Ternary_If_Expr :: struct {
using node: Expr,
x: ^Expr,
x: ^Expr,
op1: tokenizer.Token,
cond: ^Expr,
cond: ^Expr,
op2: tokenizer.Token,
y: ^Expr,
}
@@ -217,7 +225,7 @@ Ternary_When_Expr :: struct {
using node: Expr,
x: ^Expr,
op1: tokenizer.Token,
cond: ^Expr,
cond: ^Expr,
op2: tokenizer.Token,
y: ^Expr,
}
@@ -561,7 +569,6 @@ Distinct_Type :: struct {
Opaque_Type :: struct {
using node: Expr,
tok: tokenizer.Token_Kind,
type: ^Expr,
}
+123 -44
View File
@@ -190,6 +190,50 @@ peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0)
return;
}
peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
prev_parser := p^;
defer p^ = prev_parser;
p.tok.err = nil;
for i := 0; i <= lookahead; i += 1 {
advance_token(p);
}
tok = p.curr_tok;
return;
}
skip_possible_newline :: proc(p: ^Parser) -> bool {
if .Insert_Semicolon not_in p.tok.flags {
return false;
}
prev := p.curr_tok;
if tokenizer.is_newline(prev) {
advance_token(p);
return true;
}
return false;
}
skip_possible_newline_for_literal :: proc(p: ^Parser) -> bool {
if .Insert_Semicolon not_in p.tok.flags {
return false;
}
curr_pos := p.curr_tok.pos;
if tokenizer.is_newline(p.curr_tok) {
next := peek_token(p);
if curr_pos.line+1 >= next.pos.line {
#partial switch next.kind {
case .Open_Brace, .Else, .Where:
advance_token(p);
return true;
}
}
}
return false;
}
next_token0 :: proc(p: ^Parser) -> bool {
p.curr_tok = tokenizer.scan(&p.tok);
@@ -280,7 +324,7 @@ expect_token :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> tokenizer.Token
prev := p.curr_tok;
if prev.kind != kind {
e := tokenizer.to_string(kind);
g := tokenizer.to_string(prev.kind);
g := tokenizer.token_to_string(prev);
error(p, prev.pos, "expected '%s', got '%s'", e, g);
}
advance_token(p);
@@ -291,7 +335,7 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
prev := p.curr_tok;
if prev.kind != kind {
e := tokenizer.to_string(kind);
g := tokenizer.to_string(prev.kind);
g := tokenizer.token_to_string(prev);
error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g);
}
advance_token(p);
@@ -300,8 +344,10 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
expect_operator :: proc(p: ^Parser) -> tokenizer.Token {
prev := p.curr_tok;
if !tokenizer.is_operator(prev.kind) {
g := tokenizer.to_string(prev.kind);
if prev.kind == .If || prev.kind == .When {
// okay
} else if !tokenizer.is_operator(prev.kind) {
g := tokenizer.token_to_string(prev);
error(p, prev.pos, "expected an operator, got '%s'", g);
}
advance_token(p);
@@ -398,7 +444,16 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
}
if node != nil {
if prev.pos.line != p.curr_tok.pos.line {
if .Insert_Semicolon in p.tok.flags {
#partial switch p.curr_tok.kind {
case .Close_Brace, .Close_Paren, .Else, .EOF:
return true;
}
if is_semicolon_optional_for_node(p, node) {
return true;
}
} else if prev.pos.line != p.curr_tok.pos.line {
if is_semicolon_optional_for_node(p, node) {
return true;
}
@@ -418,7 +473,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
}
}
error(p, prev.pos, "expected ';', got %s", tokenizer.to_string(p.curr_tok.kind));
error(p, prev.pos, "expected ';', got %s", tokenizer.token_to_string(p.curr_tok));
return false;
}
@@ -491,6 +546,7 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_block_stmt(p, true);
skip_possible_newline_for_literal(p);
}
if allow_token(p, .Else) {
@@ -566,6 +622,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_block_stmt(p, false);
skip_possible_newline_for_literal(p);
}
if allow_token(p, .Else) {
@@ -627,6 +684,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_body(p);
skip_possible_newline_for_literal(p);
}
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end);
@@ -661,6 +719,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_body(p);
skip_possible_newline_for_literal(p);
}
@@ -838,6 +897,8 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
attribute.elems = elems[:];
attribute.close = close.pos;
skip_possible_newline(p);
decl := parse_stmt(p);
switch d in &decl.derived {
case ast.Value_Decl:
@@ -1026,10 +1087,11 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
body = convert_stmt_to_body(p, parse_stmt(p));
} else {
body = parse_block_stmt(p, false);
skip_possible_newline_for_literal(p);
}
if bad_stmt {
return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
}
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end);
@@ -1204,7 +1266,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
tok := advance_token(p);
error(p, tok.pos, "expected a statement, got %s", tokenizer.to_string(tok.kind));
error(p, tok.pos, "expected a statement, got %s", tokenizer.token_to_string(tok));
s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
return s;
}
@@ -1957,13 +2019,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
bl.tok = tok;
return bl;
case .Size_Of, .Align_Of, .Offset_Of:
tok := advance_token(p);
expr := ast.new(ast.Implicit, tok.pos, end_pos(tok));
expr.tok = tok;
return parse_call_expr(p, expr);
case .Open_Brace:
if !lhs {
return parse_literal_value(p, nil);
@@ -1992,15 +2047,22 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
case .Opaque:
tok := advance_token(p);
warn(p, tok.pos, "opaque is deprecated in favour of #opaque");
type := parse_type(p);
ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
ot.tok = tok.kind;
ot.type = type;
return ot;
case .Hash:
tok := expect_token(p, .Hash);
name := expect_token(p, .Ident);
switch name.text {
case "opaque":
type := parse_type(p);
ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
ot.type = type;
return ot;
case "type":
type := parse_type(p);
hp := ast.new(ast.Helper_Type, tok.pos, type.end);
@@ -2156,7 +2218,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
where_token: tokenizer.Token;
where_clauses: []^ast.Expr;
if (p.curr_tok.kind == .Where) {
skip_possible_newline_for_literal(p);
if p.curr_tok.kind == .Where {
where_token = expect_token(p, .Where);
prev_level := p.expr_level;
p.expr_level = -1;
@@ -2225,25 +2290,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
ti.specialization = nil;
return ti;
case .Type_Of:
tok := advance_token(p);
i := ast.new(ast.Implicit, tok.pos, end_pos(tok));
i.tok = tok;
type: ^ast.Expr = parse_call_expr(p, i);
for p.curr_tok.kind == .Period {
period := advance_token(p);
field := parse_ident(p);
sel := ast.new(ast.Selector_Expr, period.pos, field.end);
sel.expr = type;
sel.field = field;
type = sel;
}
return type;
case .Pointer:
tok := expect_token(p, .Pointer);
elem := parse_type(p);
@@ -2351,12 +2397,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
where_token: tokenizer.Token;
where_clauses: []^ast.Expr;
if (p.curr_tok.kind == .Where) {
skip_possible_newline_for_literal(p);
if p.curr_tok.kind == .Where {
where_token = expect_token(p, .Where);
prev_level := p.expr_level;
where_prev_level := p.expr_level;
p.expr_level = -1;
where_clauses = parse_rhs_expr_list(p);
p.expr_level = prev_level;
p.expr_level = where_prev_level;
}
expect_token(p, .Open_Brace);
@@ -2414,12 +2463,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
where_token: tokenizer.Token;
where_clauses: []^ast.Expr;
if (p.curr_tok.kind == .Where) {
skip_possible_newline_for_literal(p);
if p.curr_tok.kind == .Where {
where_token = expect_token(p, .Where);
prev_level := p.expr_level;
where_prev_level := p.expr_level;
p.expr_level = -1;
where_clauses = parse_rhs_expr_list(p);
p.expr_level = prev_level;
p.expr_level = where_prev_level;
}
variants: [dynamic]^ast.Expr;
@@ -2628,7 +2680,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
return lit;
}
parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr {
parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
args: [dynamic]^ast.Expr;
ellipsis: tokenizer.Token;
@@ -2686,6 +2738,14 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr {
ce.ellipsis = ellipsis;
ce.close = close.pos;
o := ast.unparen_expr(operand);
if se, ok := o.derived.(ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end);
sce.expr = o;
sce.call = ce;
return sce;
}
return ce;
}
@@ -2739,7 +2799,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
case .Colon:
interval = advance_token(p);
is_slice_op = true;
if (p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF) {
if p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF {
indicies[1] = parse_expr(p, false);
}
}
@@ -2776,6 +2836,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
sel.expr = operand;
sel.op = tok;
sel.field = field;
operand = sel;
@@ -2811,6 +2872,24 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
}
case .Arrow_Right:
tok := expect_token(p, .Arrow_Right);
#partial switch p.curr_tok.kind {
case .Ident:
field := parse_ident(p);
sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
sel.expr = operand;
sel.op = tok;
sel.field = field;
operand = sel;
case:
error(p, p.curr_tok.pos, "expected a selector");
advance_token(p);
operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
}
case .Pointer:
op := expect_token(p, .Pointer);
deref := ast.new(ast.Deref_Expr, operand.pos, end_pos(op));
+13 -12
View File
@@ -133,7 +133,6 @@ Token_Kind :: enum u32 {
Defer,
Return,
Proc,
Macro,
Struct,
Union,
Enum,
@@ -150,11 +149,6 @@ Token_Kind :: enum u32 {
Inline,
No_Inline,
Context,
Size_Of,
Align_Of,
Offset_Of,
Type_Of,
Const,
B_Keyword_End,
COUNT,
@@ -268,7 +262,6 @@ tokens := [Token_Kind.COUNT]string {
"defer",
"return",
"proc",
"macro",
"struct",
"union",
"enum",
@@ -285,16 +278,24 @@ tokens := [Token_Kind.COUNT]string {
"inline",
"no_inline",
"context",
"size_of",
"align_of",
"offset_of",
"type_of",
"const",
"",
};
custom_keyword_tokens: []string;
is_newline :: proc(tok: Token) -> bool {
return tok.kind == .Semicolon && tok.text == "\n";
}
token_to_string :: proc(tok: Token) -> string {
if is_newline(tok) {
return "newline";
}
return to_string(tok.kind);
}
to_string :: proc(kind: Token_Kind) -> string {
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
return tokens[kind];
+64 -15
View File
@@ -1,22 +1,31 @@
package odin_tokenizer
import "core:fmt"
import "core:unicode"
import "core:unicode/utf8"
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
Flag :: enum {
Insert_Semicolon,
}
Flags :: distinct bit_set[Flag; u32];
Tokenizer :: struct {
// Immutable data
path: string,
src: []byte,
err: Error_Handler,
flags: Flags,
// Tokenizing state
ch: rune,
offset: int,
read_offset: int,
line_offset: int,
line_count: int,
insert_semicolon: bool,
// Mutable data
error_count: int,
@@ -105,11 +114,18 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
}
skip_whitespace :: proc(t: ^Tokenizer) {
for t.ch == ' ' ||
t.ch == '\t' ||
t.ch == '\n' ||
t.ch == '\r' {
advance_rune(t);
for {
switch t.ch {
case ' ', '\t', '\r':
advance_rune(t);
case '\n':
if t.insert_semicolon {
return;
}
advance_rune(t);
case:
return;
}
}
}
@@ -122,12 +138,13 @@ is_letter :: proc(r: rune) -> bool {
return true;
}
}
// TODO(bill): Add unicode lookup tables
return false;
return unicode.is_letter(r);
}
is_digit :: proc(r: rune) -> bool {
// TODO(bill): Add unicode lookup tables
return '0' <= r && r <= '9';
if '0' <= r && r <= '9' {
return true;
}
return unicode.is_digit(r);
}
@@ -491,6 +508,8 @@ scan :: proc(t: ^Tokenizer) -> Token {
lit: string;
pos := offset_to_pos(t, offset);
insert_semicolon := false;
switch ch := t.ch; true {
case is_letter(ch):
lit = scan_identifier(t);
@@ -509,24 +528,39 @@ scan :: proc(t: ^Tokenizer) -> Token {
break check_keyword;
}
}
if kind == .Ident && lit == "notin" {
kind = .Not_In;
#partial switch kind {
case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return:
insert_semicolon = true;
}
}
case '0' <= ch && ch <= '9':
insert_semicolon = true;
kind, lit = scan_number(t, false);
case:
advance_rune(t);
switch ch {
case -1:
kind = .EOF;
if t.insert_semicolon {
t.insert_semicolon = false;
kind = .Semicolon;
lit = "\n";
}
case '\n':
t.insert_semicolon = false;
kind = .Semicolon;
lit = "\n";
case '"':
insert_semicolon = true;
kind = .String;
lit = scan_string(t);
case '\'':
insert_semicolon = true;
kind = .Rune;
lit = scan_rune(t);
case '`':
insert_semicolon = true;
kind = .String;
lit = scan_raw_string(t);
case '=':
@@ -540,10 +574,13 @@ scan :: proc(t: ^Tokenizer) -> Token {
case '#':
kind = .Hash;
if t.ch == '!' {
insert_semicolon = t.insert_semicolon;
kind = .Comment;
lit = scan_comment(t);
}
case '?': kind = .Question;
case '?':
insert_semicolon = true;
kind = .Question;
case '@': kind = .At;
case '$': kind = .Dollar;
case '^': kind = .Pointer;
@@ -562,6 +599,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
case '*': kind = switch2(t, .Mul, .Mul_Eq);
case '/':
if t.ch == '/' || t.ch == '*' {
insert_semicolon = t.insert_semicolon;
kind = .Comment;
lit = scan_comment(t);
} else {
@@ -604,11 +642,17 @@ scan :: proc(t: ^Tokenizer) -> Token {
case ',': kind = .Comma;
case ';': kind = .Semicolon;
case '(': kind = .Open_Paren;
case ')': kind = .Close_Paren;
case ')':
insert_semicolon = true;
kind = .Close_Paren;
case '[': kind = .Open_Bracket;
case ']': kind = .Close_Bracket;
case ']':
insert_semicolon = true;
kind = .Close_Bracket;
case '{': kind = .Open_Brace;
case '}': kind = .Close_Brace;
case '}':
insert_semicolon = true;
kind = .Close_Brace;
case '\\': kind = .Back_Slash;
@@ -616,10 +660,15 @@ scan :: proc(t: ^Tokenizer) -> Token {
if ch != utf8.RUNE_BOM {
error(t, t.offset, "illegal character '%r': %d", ch, ch);
}
insert_semicolon = t.insert_semicolon; // preserve insert_semicolon info
kind = .Invalid;
}
}
if .Insert_Semicolon in t.flags {
t.insert_semicolon = insert_semicolon;
}
if lit == "" {
lit = string(t.src[offset : t.offset]);
}
+69
View File
@@ -0,0 +1,69 @@
package os
import "core:io"
stream_from_handle :: proc(fd: Handle) -> io.Stream {
s: io.Stream;
s.stream_data = rawptr(uintptr(fd));
s.stream_vtable = _file_stream_vtable;
return s;
}
@(private)
_file_stream_vtable := &io.Stream_VTable{
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
fd := Handle(uintptr(s.stream_data));
os_err: Errno;
n, os_err = read(fd, p);
return;
},
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
when ODIN_OS == "windows" {
fd := Handle(uintptr(s.stream_data));
os_err: Errno;
n, os_err = read_at(fd, p, offset);
}
return;
},
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
fd := Handle(uintptr(s.stream_data));
os_err: Errno;
n, os_err = write(fd, p);
return;
},
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
when ODIN_OS == "windows" {
fd := Handle(uintptr(s.stream_data));
os_err: Errno;
n, os_err = write_at(fd, p, offset);
_ = os_err;
}
return;
},
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
fd := Handle(uintptr(s.stream_data));
n, os_err := seek(fd, offset, int(whence));
_ = os_err;
return n, nil;
},
impl_size = proc(s: io.Stream) -> i64 {
fd := Handle(uintptr(s.stream_data));
sz, _ := file_size(fd);
return sz;
},
impl_flush = proc(s: io.Stream) -> io.Error {
when ODIN_OS == "windows" {
fd := Handle(uintptr(s.stream_data));
flush(fd);
} else {
// TOOD(bill): other operating systems
}
return nil;
},
impl_close = proc(s: io.Stream) -> io.Error {
fd := Handle(uintptr(s.stream_data));
close(fd);
return nil;
},
};
+2 -1
View File
@@ -8,7 +8,8 @@ SEPARATOR :: '\\';
SEPARATOR_STRING :: `\`;
LIST_SEPARATOR :: ';';
reserved_names := []string{
@(private)
reserved_names := [?]string{
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
-2
View File
@@ -28,8 +28,6 @@ Match_Error :: enum {
// match requires that the pattern matches the entirety of the name, not just a substring
// The only possible error returned is .Syntax_Error
//
// NOTE(bill): This is effectively the shell pattern matching system found
//
match :: proc(pattern, name: string) -> (matched: bool, err: Match_Error) {
pattern, name := pattern, name;
pattern_loop: for len(pattern) > 0 {
+79
View File
@@ -1206,3 +1206,82 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
return;
}
/*
not_equal :: proc(a, b: any) -> bool {
return !equal(a, b);
}
equal :: proc(a, b: any) -> bool {
if a == nil && b == nil {
return true;
}
if a.id != b.id {
return false;
}
if a.data == b.data {
return true;
}
t := type_info_of(a.id);
if .Comparable not_in t.flags {
return false;
}
if t.size == 0 {
return true;
}
if .Simple_Compare in t.flags {
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0;
}
t = runtime.type_info_core(t);
#partial switch v in t.variant {
case Type_Info_String:
if v.is_cstring {
x := string((^cstring)(a.data)^);
y := string((^cstring)(b.data)^);
return x == y;
} else {
x := (^string)(a.data)^;
y := (^string)(b.data)^;
return x == y;
}
case Type_Info_Array:
for i in 0..<v.count {
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
return false;
}
}
case Type_Info_Enumerated_Array:
for i in 0..<v.count {
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
return false;
}
}
case Type_Info_Struct:
if v.equal != nil {
return v.equal(a.data, b.data);
} else {
for offset, i in v.offsets {
x := rawptr(uintptr(a.data) + offset);
y := rawptr(uintptr(b.data) + offset);
id := v.types[i].id;
if !equal(any{x, id}, any{y, id}) {
return false;
}
}
}
}
return true;
}
*/
+161 -132
View File
@@ -1,5 +1,6 @@
package reflect
import "core:io"
import "core:strings"
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
@@ -218,6 +219,14 @@ is_unsigned :: proc(info: ^Type_Info) -> bool {
return false;
}
is_byte :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
#partial switch i in type_info_base(info).variant {
case Type_Info_Integer: return info.size == 1;
}
return false;
}
is_integer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
@@ -352,258 +361,278 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) {
write_type(buf, type_info_of(id));
}
write_typeid_writer :: proc(writer: io.Writer, id: typeid) {
write_type(writer, type_info_of(id));
}
write_type :: proc(buf: ^strings.Builder, ti: ^Type_Info) {
write_typeid :: proc{
write_typeid_builder,
write_typeid_writer,
};
write_type :: proc{
write_type_builder,
write_type_writer,
};
write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) -> int {
return write_type_writer(strings.to_writer(buf), ti);
}
write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) {
using strings;
if ti == nil {
write_string(buf, "nil");
return;
return write_string(w, "nil");
}
_n1 :: proc(err: io.Error) -> int { return 1 if err == nil else 0; };
_n2 :: proc(n: int, _: io.Error) -> int { return n; };
_n :: proc{_n1, _n2};
switch info in ti.variant {
case Type_Info_Named:
write_string(buf, info.name);
return write_string(w, info.name);
case Type_Info_Integer:
switch ti.id {
case int: write_string(buf, "int");
case uint: write_string(buf, "uint");
case uintptr: write_string(buf, "uintptr");
case int: return write_string(w, "int");
case uint: return write_string(w, "uint");
case uintptr: return write_string(w, "uintptr");
case:
write_byte(buf, 'i' if info.signed else 'u');
write_i64(buf, i64(8*ti.size), 10);
n += _n(io.write_byte(w, 'i' if info.signed else 'u'));
n += _n(io.write_i64(w, i64(8*ti.size), 10));
switch info.endianness {
case .Platform: // Okay
case .Little: write_string(buf, "le");
case .Big: write_string(buf, "be");
case .Little: n += write_string(w, "le");
case .Big: n += write_string(w, "be");
}
}
case Type_Info_Rune:
write_string(buf, "rune");
n += _n(io.write_string(w, "rune"));
case Type_Info_Float:
write_byte(buf, 'f');
write_i64(buf, i64(8*ti.size), 10);
n += _n(io.write_byte(w, 'f'));
n += _n(io.write_i64(w, i64(8*ti.size), 10));
switch info.endianness {
case .Platform: // Okay
case .Little: write_string(buf, "le");
case .Big: write_string(buf, "be");
case .Little: n += write_string(w, "le");
case .Big: n += write_string(w, "be");
}
case Type_Info_Complex:
write_string(buf, "complex");
write_i64(buf, i64(8*ti.size), 10);
n += _n(io.write_string(w, "complex"));
n += _n(io.write_i64(w, i64(8*ti.size), 10));
case Type_Info_Quaternion:
write_string(buf, "quaternion");
write_i64(buf, i64(8*ti.size), 10);
n += _n(io.write_string(w, "quaternion"));
n += _n(io.write_i64(w, i64(8*ti.size), 10));
case Type_Info_String:
if info.is_cstring {
write_string(buf, "cstring");
n += write_string(w, "cstring");
} else {
write_string(buf, "string");
n += write_string(w, "string");
}
case Type_Info_Boolean:
switch ti.id {
case bool: write_string(buf, "bool");
case bool: n += write_string(w, "bool");
case:
write_byte(buf, 'b');
write_i64(buf, i64(8*ti.size), 10);
n += _n(io.write_byte(w, 'b'));
n += _n(io.write_i64(w, i64(8*ti.size), 10));
}
case Type_Info_Any:
write_string(buf, "any");
n += write_string(w, "any");
case Type_Info_Type_Id:
write_string(buf, "typeid");
n += write_string(w, "typeid");
case Type_Info_Pointer:
if info.elem == nil {
write_string(buf, "rawptr");
write_string(w, "rawptr");
} else {
write_string(buf, "^");
write_type(buf, info.elem);
write_string(w, "^");
write_type(w, info.elem);
}
case Type_Info_Procedure:
write_string(buf, "proc");
n += write_string(w, "proc");
if info.params == nil {
write_string(buf, "()");
n += write_string(w, "()");
} else {
t := info.params.variant.(Type_Info_Tuple);
write_string(buf, "(");
n += write_string(w, "(");
for t, i in t.types {
if i > 0 {
write_string(buf, ", ");
n += write_string(w, ", ");
}
write_type(buf, t);
n += write_type(w, t);
}
write_string(buf, ")");
n += write_string(w, ")");
}
if info.results != nil {
write_string(buf, " -> ");
write_type(buf, info.results);
n += write_string(w, " -> ");
n += write_type(w, info.results);
}
case Type_Info_Tuple:
count := len(info.names);
if count != 1 { write_string(buf, "("); }
if count != 1 { n += write_string(w, "("); }
for name, i in info.names {
if i > 0 { write_string(buf, ", "); }
if i > 0 { n += write_string(w, ", "); }
t := info.types[i];
if len(name) > 0 {
write_string(buf, name);
write_string(buf, ": ");
n += write_string(w, name);
n += write_string(w, ": ");
}
write_type(buf, t);
n += write_type(w, t);
}
if count != 1 { write_string(buf, ")"); }
if count != 1 { n += write_string(w, ")"); }
case Type_Info_Array:
write_string(buf, "[");
write_i64(buf, i64(info.count), 10);
write_string(buf, "]");
write_type(buf, info.elem);
n += _n(io.write_string(w, "["));
n += _n(io.write_i64(w, i64(info.count), 10));
n += _n(io.write_string(w, "]"));
n += write_type(w, info.elem);
case Type_Info_Enumerated_Array:
write_string(buf, "[");
write_type(buf, info.index);
write_string(buf, "]");
write_type(buf, info.elem);
n += write_string(w, "[");
n += write_type(w, info.index);
n += write_string(w, "]");
n += write_type(w, info.elem);
case Type_Info_Dynamic_Array:
write_string(buf, "[dynamic]");
write_type(buf, info.elem);
n += _n(io.write_string(w, "[dynamic]"));
n += write_type(w, info.elem);
case Type_Info_Slice:
write_string(buf, "[]");
write_type(buf, info.elem);
n += _n(io.write_string(w, "[]"));
n += write_type(w, info.elem);
case Type_Info_Map:
write_string(buf, "map[");
write_type(buf, info.key);
write_byte(buf, ']');
write_type(buf, info.value);
n += _n(io.write_string(w, "map["));
n += write_type(w, info.key);
n += _n(io.write_byte(w, ']'));
n += write_type(w, info.value);
case Type_Info_Struct:
switch info.soa_kind {
case .None: // Ignore
case .Fixed:
write_string(buf, "#soa[");
write_i64(buf, i64(info.soa_len));
write_byte(buf, ']');
write_type(buf, info.soa_base_type);
n += _n(io.write_string(w, "#soa["));
n += _n(io.write_i64(w, i64(info.soa_len)));
n += _n(io.write_byte(w, ']'));
n += write_type(w, info.soa_base_type);
return;
case .Slice:
write_string(buf, "#soa[]");
write_type(buf, info.soa_base_type);
n += _n(io.write_string(w, "#soa[]"));
n += write_type(w, info.soa_base_type);
return;
case .Dynamic:
write_string(buf, "#soa[dynamic]");
write_type(buf, info.soa_base_type);
n += _n(io.write_string(w, "#soa[dynamic]"));
n += write_type(w, info.soa_base_type);
return;
}
write_string(buf, "struct ");
if info.is_packed { write_string(buf, "#packed "); }
if info.is_raw_union { write_string(buf, "#raw_union "); }
n += write_string(w, "struct ");
if info.is_packed { n += write_string(w, "#packed "); }
if info.is_raw_union { n += write_string(w, "#raw_union "); }
if info.custom_align {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
n += _n(io.write_string(w, "#align "));
n += _n(io.write_i64(w, i64(ti.align), 10));
n += _n(io.write_byte(w, ' '));
}
write_byte(buf, '{');
n += _n(io.write_byte(w, '{'));
for name, i in info.names {
if i > 0 { write_string(buf, ", "); }
write_string(buf, name);
write_string(buf, ": ");
write_type(buf, info.types[i]);
if i > 0 { n += write_string(w, ", "); }
n += _n(io.write_string(w, name));
n += _n(io.write_string(w, ": "));
n += write_type(w, info.types[i]);
}
write_byte(buf, '}');
n += _n(io.write_byte(w, '}'));
case Type_Info_Union:
write_string(buf, "union ");
n += write_string(w, "union ");
if info.custom_align {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
n += write_string(w, "#align ");
n += _n(io.write_i64(w, i64(ti.align), 10));
n += _n(io.write_byte(w, ' '));
}
write_byte(buf, '{');
n += _n(io.write_byte(w, '{'));
for variant, i in info.variants {
if i > 0 { write_string(buf, ", "); }
write_type(buf, variant);
if i > 0 { n += write_string(w, ", "); }
n += write_type(w, variant);
}
write_byte(buf, '}');
n += _n(io.write_byte(w, '}'));
case Type_Info_Enum:
write_string(buf, "enum ");
write_type(buf, info.base);
write_string(buf, " {");
n += write_string(w, "enum ");
n += write_type(w, info.base);
n += write_string(w, " {");
for name, i in info.names {
if i > 0 { write_string(buf, ", "); }
write_string(buf, name);
if i > 0 { n += write_string(w, ", "); }
n += write_string(w, name);
}
write_byte(buf, '}');
n += _n(io.write_byte(w, '}'));
case Type_Info_Bit_Field:
write_string(buf, "bit_field ");
n += write_string(w, "bit_field ");
if ti.align != 1 {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
n += write_string(w, "#align ");
n += _n(io.write_i64(w, i64(ti.align), 10));
n += _n(io.write_byte(w, ' '));
}
write_string(buf, " {");
n += write_string(w, " {");
for name, i in info.names {
if i > 0 { write_string(buf, ", "); }
write_string(buf, name);
write_string(buf, ": ");
write_i64(buf, i64(info.bits[i]), 10);
if i > 0 { n += write_string(w, ", "); }
n += write_string(w, name);
n += write_string(w, ": ");
n += _n(io.write_i64(w, i64(info.bits[i]), 10));
}
write_byte(buf, '}');
n += _n(io.write_byte(w, '}'));
case Type_Info_Bit_Set:
write_string(buf, "bit_set[");
n += write_string(w, "bit_set[");
switch {
case is_enum(info.elem):
write_type(buf, info.elem);
n += write_type(w, info.elem);
case is_rune(info.elem):
write_encoded_rune(buf, rune(info.lower));
write_string(buf, "..");
write_encoded_rune(buf, rune(info.upper));
n += write_encoded_rune(w, rune(info.lower));
n += write_string(w, "..");
n += write_encoded_rune(w, rune(info.upper));
case:
write_i64(buf, info.lower, 10);
write_string(buf, "..");
write_i64(buf, info.upper, 10);
n += _n(io.write_i64(w, info.lower, 10));
n += write_string(w, "..");
n += _n(io.write_i64(w, info.upper, 10));
}
if info.underlying != nil {
write_string(buf, "; ");
write_type(buf, info.underlying);
n += write_string(w, "; ");
n += write_type(w, info.underlying);
}
write_byte(buf, ']');
n += _n(io.write_byte(w, ']'));
case Type_Info_Opaque:
write_string(buf, "opaque ");
write_type(buf, info.elem);
n += write_string(w, "#opaque ");
n += write_type(w, info.elem);
case Type_Info_Simd_Vector:
if info.is_x86_mmx {
write_string(buf, "intrinsics.x86_mmx");
n += write_string(w, "intrinsics.x86_mmx");
} else {
write_string(buf, "#simd[");
write_i64(buf, i64(info.count));
write_byte(buf, ']');
write_type(buf, info.elem);
n += write_string(w, "#simd[");
n += _n(io.write_i64(w, i64(info.count)));
n += _n(io.write_byte(w, ']'));
n += write_type(w, info.elem);
}
case Type_Info_Relative_Pointer:
write_string(buf, "#relative(");
write_type(buf, info.base_integer);
write_string(buf, ") ");
write_type(buf, info.pointer);
n += write_string(w, "#relative(");
n += write_type(w, info.base_integer);
n += write_string(w, ") ");
n += write_type(w, info.pointer);
case Type_Info_Relative_Slice:
write_string(buf, "#relative(");
write_type(buf, info.base_integer);
write_string(buf, ") ");
write_type(buf, info.slice);
n += write_string(w, "#relative(");
n += write_type(w, info.base_integer);
n += write_string(w, ") ");
n += write_type(w, info.slice);
}
return;
}
+32 -1282
View File
File diff suppressed because it is too large Load Diff
+838
View File
@@ -0,0 +1,838 @@
package runtime
@builtin
Maybe :: union(T: typeid) #maybe {T};
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator;
@builtin
init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator);
}
@builtin
copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
n := max(0, min(len(dst), len(src)));
if n > 0 {
mem_copy(raw_data(dst), raw_data(src), n*size_of(E));
}
return n;
}
@builtin
copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
n := max(0, min(len(dst), len(src)));
if n > 0 {
mem_copy(raw_data(dst), raw_data(src), n);
}
return n;
}
@builtin
copy :: proc{copy_slice, copy_from_string};
@builtin
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
bounds_check_error_loc(loc, index, len(array));
n := len(array)-1;
if index != n {
array[index] = array[n];
}
pop(array);
}
@builtin
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
bounds_check_error_loc(loc, index, len(array));
if index+1 < len(array) {
copy(array[index:], array[index+1:]);
}
pop(array);
}
@builtin
remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) {
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array));
n := max(hi-lo, 0);
if n > 0 {
if hi != len(array) {
copy(array[lo:], array[hi:]);
}
(^Raw_Dynamic_Array)(array).len -= n;
}
}
@builtin
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
assert(len(array) > 0, "", loc);
res = array[len(array)-1];
(^Raw_Dynamic_Array)(array).len -= 1;
return res;
}
@builtin
pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
if len(array) == 0 {
return;
}
res, ok = array[len(array)-1], true;
(^Raw_Dynamic_Array)(array).len -= 1;
return;
}
@builtin
pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
assert(len(array) > 0, "", loc);
res = array[0];
if len(array) > 1 {
copy(array[0:], array[1:]);
}
(^Raw_Dynamic_Array)(array).len -= 1;
return res;
}
@builtin
pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
if len(array) == 0 {
return;
}
res, ok = array[0], true;
if len(array) > 1 {
copy(array[0:], array[1:]);
}
(^Raw_Dynamic_Array)(array).len -= 1;
return;
}
@builtin
clear :: proc{clear_dynamic_array, clear_map};
@builtin
reserve :: proc{reserve_dynamic_array, reserve_map};
@builtin
resize :: proc{resize_dynamic_array};
@builtin
free :: proc{mem_free};
@builtin
free_all :: proc{mem_free_all};
@builtin
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
mem_free(raw_data(str), allocator, loc);
}
@builtin
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
mem_free((^byte)(str), allocator, loc);
}
@builtin
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
mem_free(raw_data(array), array.allocator, loc);
}
@builtin
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
mem_free(raw_data(array), allocator, loc);
}
@builtin
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
raw := transmute(Raw_Map)m;
delete_slice(raw.hashes);
mem_free(raw.entries.data, raw.entries.allocator, loc);
}
@builtin
delete :: proc{
delete_string,
delete_cstring,
delete_dynamic_array,
delete_slice,
delete_map,
};
// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
@builtin
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
if ptr != nil { ptr^ = T{}; }
return ptr;
}
@builtin
new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
if ptr != nil { ptr^ = data; }
return ptr;
}
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
make_slice_error_loc(loc, len);
data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
if data == nil && size_of(E) != 0 {
return nil;
}
// mem_zero(data, size_of(E)*len);
s := Raw_Slice{data, len};
return transmute(T)s;
}
@builtin
make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
return make_aligned(T, len, align_of(E), allocator, loc);
}
@builtin
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
}
@builtin
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
}
@builtin
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
make_dynamic_array_error_loc(loc, len, cap);
data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
s := Raw_Dynamic_Array{data, len, cap, allocator};
if data == nil && size_of(E) != 0 {
s.len, s.cap = 0, 0;
}
// mem_zero(data, size_of(E)*cap);
return transmute(T)s;
}
@builtin
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
make_map_expr_error_loc(loc, cap);
context.allocator = allocator;
m: T;
reserve_map(&m, cap);
return m;
}
// The make built-in procedure allocates and initializes a value of type slice, dynamic array, or map (only)
// Similar to new, the first argument is a type, not a value. Unlike new, make's return type is the same as the
// type of its argument, not a pointer to it.
// Make uses the specified allocator, default is context.allocator, default is context.allocator
@builtin
make :: proc{
make_slice,
make_dynamic_array,
make_dynamic_array_len,
make_dynamic_array_len_cap,
make_map,
};
@builtin
clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) {
if m == nil {
return;
}
raw_map := (^Raw_Map)(m);
entries := (^Raw_Dynamic_Array)(&raw_map.entries);
entries.len = 0;
for _, i in raw_map.hashes {
raw_map.hashes[i] = -1;
}
}
@builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
if m != nil {
__dynamic_map_reserve(__get_map_header(m), capacity);
}
}
// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map.
// If m is nil, or there is no such element, this procedure is a no-op
@builtin
delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
if m != nil {
key := key;
__dynamic_map_delete_key(__get_map_header(m), __get_map_hash(&key));
}
}
@builtin
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
if array == nil {
return;
}
arg_len := 1;
if cap(array) < len(array)+arg_len {
cap := 2 * cap(array) + max(8, arg_len);
_ = reserve(array, cap, loc);
}
arg_len = min(cap(array)-len(array), arg_len);
if arg_len > 0 {
a := (^Raw_Dynamic_Array)(array);
if size_of(E) != 0 {
data := (^E)(a.data);
assert(data != nil);
val := arg;
mem_copy(ptr_offset(data, a.len), &val, size_of(E));
}
a.len += arg_len;
}
}
@builtin
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) {
if array == nil {
return;
}
arg_len := len(args);
if arg_len <= 0 {
return;
}
if cap(array) < len(array)+arg_len {
cap := 2 * cap(array) + max(8, arg_len);
_ = reserve(array, cap, loc);
}
arg_len = min(cap(array)-len(array), arg_len);
if arg_len > 0 {
a := (^Raw_Dynamic_Array)(array);
if size_of(E) != 0 {
data := (^E)(a.data);
assert(data != nil);
mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
}
a.len += arg_len;
}
}
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
@builtin
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
args := transmute([]E)arg;
append_elems(array=array, args=args, loc=loc);
}
@builtin
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
if array == nil {
return false;
}
old_cap := cap(array);
if capacity <= old_cap {
return true;
}
if array.allocator.procedure == nil {
array.allocator = context.allocator;
}
assert(array.allocator.procedure != nil);
ti := type_info_of(typeid_of(T));
ti = type_info_base(ti);
si := &ti.variant.(Type_Info_Struct);
field_count := uintptr(len(si.offsets) - 3);
if field_count == 0 {
return true;
}
cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr));
assert(cap_ptr^ == old_cap);
old_size := 0;
new_size := 0;
max_align := 0;
for i in 0..<field_count {
type := si.types[i].variant.(Type_Info_Pointer).elem;
max_align = max(max_align, type.align);
old_size = align_forward_int(old_size, type.align);
new_size = align_forward_int(new_size, type.align);
old_size += type.size * old_cap;
new_size += type.size * capacity;
}
old_size = align_forward_int(old_size, max_align);
new_size = align_forward_int(new_size, max_align);
old_data := (^rawptr)(array)^;
new_data := array.allocator.procedure(
array.allocator.data, .Alloc, new_size, max_align,
nil, old_size, 0, loc,
);
if new_data == nil {
return false;
}
cap_ptr^ = capacity;
old_offset := 0;
new_offset := 0;
for i in 0..<field_count {
type := si.types[i].variant.(Type_Info_Pointer).elem;
max_align = max(max_align, type.align);
old_offset = align_forward_int(old_offset, type.align);
new_offset = align_forward_int(new_offset, type.align);
new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
(^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
old_offset += type.size * old_cap;
new_offset += type.size * capacity;
}
array.allocator.procedure(
array.allocator.data, .Free, 0, max_align,
old_data, old_size, 0, loc,
);
return true;
}
@builtin
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
if array == nil {
return;
}
arg_len := 1;
if cap(array) <= len(array)+arg_len {
cap := 2 * cap(array) + max(8, arg_len);
_ = reserve_soa(array, cap, loc);
}
arg_len = min(cap(array)-len(array), arg_len);
if arg_len > 0 {
ti := type_info_of(typeid_of(T));
ti = type_info_base(ti);
si := &ti.variant.(Type_Info_Struct);
field_count := uintptr(len(si.offsets) - 3);
if field_count == 0 {
return;
}
data := (^rawptr)(array)^;
len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
soa_offset := 0;
item_offset := 0;
arg_copy := arg;
arg_ptr := &arg_copy;
max_align := 0;
for i in 0..<field_count {
type := si.types[i].variant.(Type_Info_Pointer).elem;
max_align = max(max_align, type.align);
soa_offset = align_forward_int(soa_offset, type.align);
item_offset = align_forward_int(item_offset, type.align);
dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^));
src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
mem_copy(dst, src, type.size);
soa_offset += type.size * cap(array);
item_offset += type.size;
}
len_ptr^ += arg_len;
}
}
@builtin
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
if array == nil {
return;
}
arg_len := len(args);
if arg_len == 0 {
return;
}
if cap(array) <= len(array)+arg_len {
cap := 2 * cap(array) + max(8, arg_len);
_ = reserve_soa(array, cap, loc);
}
arg_len = min(cap(array)-len(array), arg_len);
if arg_len > 0 {
ti := type_info_of(typeid_of(T));
ti = type_info_base(ti);
si := &ti.variant.(Type_Info_Struct);
field_count := uintptr(len(si.offsets) - 3);
if field_count == 0 {
return;
}
data := (^rawptr)(array)^;
len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
soa_offset := 0;
item_offset := 0;
args_ptr := &args[0];
max_align := 0;
for i in 0..<field_count {
type := si.types[i].variant.(Type_Info_Pointer).elem;
max_align = max(max_align, type.align);
soa_offset = align_forward_int(soa_offset, type.align);
item_offset = align_forward_int(item_offset, type.align);
dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^);
src := uintptr(args_ptr) + uintptr(item_offset);
for j in 0..<arg_len {
d := rawptr(dst + uintptr(j*type.size));
s := rawptr(src + uintptr(j*size_of(E)));
mem_copy(d, s, type.size);
}
soa_offset += type.size * cap(array);
item_offset += type.size;
}
len_ptr^ += arg_len;
}
}
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
@builtin
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
for arg in args {
append(array = array, args = transmute([]E)(arg), loc = loc);
}
}
// The append built-in procedure appends elements to the end of a dynamic array
@builtin append :: proc{append_elem, append_elems, append_elem_string};
// The append_soa built-in procedure appends elements to the end of an #soa dynamic array
@builtin append_soa :: proc{append_soa_elem, append_soa_elems};
@builtin
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
if array == nil {
return;
}
resize(array, len(array)+1);
}
@builtin
insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if array == nil {
return;
}
n := len(array);
m :: 1;
resize(array, n+m, loc);
if n+m <= len(array) {
when size_of(E) != 0 {
copy(array[index+m:], array[index:]);
array[index] = arg;
}
ok = true;
}
return;
}
@builtin
insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if array == nil {
return;
}
if len(args) == 0 {
ok = true;
return;
}
n := len(array);
m := len(args);
resize(array, n+m, loc);
if n+m <= len(array) {
when size_of(E) != 0 {
copy(array[index+m:], array[index:]);
copy(array[index:], args);
}
ok = true;
}
return;
}
@builtin
insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if array == nil {
return;
}
if len(args) == 0 {
ok = true;
return;
}
n := len(array);
m := len(args);
resize(array, n+m, loc);
if n+m <= len(array) {
copy(array[index+m:], array[index:]);
copy(array[index:], args);
ok = true;
}
return;
}
@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string};
@builtin
clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) {
if array != nil {
(^Raw_Dynamic_Array)(array).len = 0;
}
}
@builtin
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
if array == nil {
return false;
}
a := (^Raw_Dynamic_Array)(array);
if capacity <= a.cap {
return true;
}
if a.allocator.procedure == nil {
a.allocator = context.allocator;
}
assert(a.allocator.procedure != nil);
old_size := a.cap * size_of(E);
new_size := capacity * size_of(E);
allocator := a.allocator;
new_data := allocator.procedure(
allocator.data, .Resize, new_size, align_of(E),
a.data, old_size, 0, loc,
);
if new_data == nil {
return false;
}
a.data = new_data;
a.cap = capacity;
return true;
}
@builtin
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
if array == nil {
return false;
}
a := (^Raw_Dynamic_Array)(array);
if length <= a.cap {
a.len = max(length, 0);
return true;
}
if a.allocator.procedure == nil {
a.allocator = context.allocator;
}
assert(a.allocator.procedure != nil);
old_size := a.cap * size_of(E);
new_size := length * size_of(E);
allocator := a.allocator;
new_data := allocator.procedure(
allocator.data, .Resize, new_size, align_of(E),
a.data, old_size, 0, loc,
);
if new_data == nil {
return false;
}
a.data = new_data;
a.len = length;
a.cap = length;
return true;
}
@builtin
incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
s^ |= {elem};
return s^;
}
@builtin
incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
for elem in elems {
s^ |= {elem};
}
return s^;
}
@builtin
incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
s^ |= other;
return s^;
}
@builtin
excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
s^ &~= {elem};
return s^;
}
@builtin
excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
for elem in elems {
s^ &~= {elem};
}
return s^;
}
@builtin
excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
s^ &~= other;
return s^;
}
@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set};
@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set};
@builtin
card :: proc(s: $S/bit_set[$E; $U]) -> int {
when size_of(S) == 1 {
foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- }
return int(count_ones(transmute(u8)s));
} else when size_of(S) == 2 {
foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
return int(count_ones(transmute(u16)s));
} else when size_of(S) == 4 {
foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
return int(count_ones(transmute(u32)s));
} else when size_of(S) == 8 {
foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
return int(count_ones(transmute(u64)s));
} else when size_of(S) == 16 {
foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- }
return int(count_ones(transmute(u128)s));
} else {
#panic("Unhandled card bit_set size");
}
}
@builtin
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
return (^E)(a);
}
@builtin
raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E {
ptr := (transmute(Raw_Slice)s).data;
return (^E)(ptr);
}
@builtin
raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E {
ptr := (transmute(Raw_Dynamic_Array)s).data;
return (^E)(ptr);
}
@builtin
raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 {
return (transmute(Raw_String)s).data;
}
@builtin
raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data};
@builtin
@(disabled=ODIN_DISABLE_ASSERT)
assert :: proc(condition: bool, message := "", loc := #caller_location) {
if !condition {
proc(message: string, loc: Source_Code_Location) {
p := context.assertion_failure_proc;
if p == nil {
p = default_assertion_failure_proc;
}
p("runtime assertion", message, loc);
}(message, loc);
}
}
@builtin
@(disabled=ODIN_DISABLE_ASSERT)
panic :: proc(message: string, loc := #caller_location) -> ! {
p := context.assertion_failure_proc;
if p == nil {
p = default_assertion_failure_proc;
}
p("panic", message, loc);
}
@builtin
@(disabled=ODIN_DISABLE_ASSERT)
unimplemented :: proc(message := "", loc := #caller_location) -> ! {
p := context.assertion_failure_proc;
if p == nil {
p = default_assertion_failure_proc;
}
p("not yet implemented", message, loc);
}
@builtin
@(disabled=ODIN_DISABLE_ASSERT)
unreachable :: proc(message := "", loc := #caller_location) -> ! {
p := context.assertion_failure_proc;
if p == nil {
p = default_assertion_failure_proc;
}
if message != "" {
p("internal error", message, loc);
} else {
p("internal error", "entered unreachable code", loc);
}
}
+100
View File
@@ -0,0 +1,100 @@
package runtime
__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) {
array := (^Raw_Dynamic_Array)(array_);
array.allocator = context.allocator;
assert(array.allocator.procedure != nil);
if cap > 0 {
__dynamic_array_reserve(array_, elem_size, elem_align, cap, loc);
array.len = len;
}
}
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool {
array := (^Raw_Dynamic_Array)(array_);
// NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
// assuming that appending/reserving will set the allocator, if it is not already set.
if array.allocator.procedure == nil {
array.allocator = context.allocator;
}
assert(array.allocator.procedure != nil);
if cap <= array.cap {
return true;
}
old_size := array.cap * elem_size;
new_size := cap * elem_size;
allocator := array.allocator;
new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
if new_data != nil || elem_size == 0 {
array.data = new_data;
array.cap = cap;
return true;
}
return false;
}
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
array := (^Raw_Dynamic_Array)(array_);
ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc);
if ok {
array.len = len;
}
return ok;
}
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
items: rawptr, item_count: int, loc := #caller_location) -> int {
array := (^Raw_Dynamic_Array)(array_);
if items == nil {
return 0;
}
if item_count <= 0 {
return 0;
}
ok := true;
if array.cap <= array.len+item_count {
cap := 2 * array.cap + max(8, item_count);
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
}
// TODO(bill): Better error handling for failed reservation
if !ok {
return array.len;
}
assert(array.data != nil);
data := uintptr(array.data) + uintptr(elem_size*array.len);
mem_copy(rawptr(data), items, elem_size * item_count);
array.len += item_count;
return array.len;
}
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int {
array := (^Raw_Dynamic_Array)(array_);
ok := true;
if array.cap <= array.len+1 {
cap := 2 * array.cap + max(8, 1);
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
}
// TODO(bill): Better error handling for failed reservation
if !ok {
return array.len;
}
assert(array.data != nil);
data := uintptr(array.data) + uintptr(elem_size*array.len);
mem_zero(rawptr(data), elem_size);
array.len += 1;
return array.len;
}
+394
View File
@@ -0,0 +1,394 @@
package runtime
import "intrinsics"
_ :: intrinsics;
INITIAL_MAP_CAP :: 16;
// Temporary data structure for comparing hashes and keys
Map_Hash :: struct {
hash: uintptr,
key_ptr: rawptr, // address of Map_Entry_Header.key
}
__get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) {
hasher := intrinsics.type_hasher_proc(K);
map_hash.key_ptr = k;
map_hash.hash = hasher(k, 0);
return;
}
__get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) {
hash.hash = entry.hash;
hash.key_ptr = rawptr(uintptr(entry) + h.key_offset);
return;
}
Map_Find_Result :: struct {
hash_index: int,
entry_prev: int,
entry_index: int,
}
Map_Entry_Header :: struct {
hash: uintptr,
next: int,
/*
key: Key_Value,
value: Value_Type,
*/
}
Map_Header :: struct {
m: ^Raw_Map,
equal: Equal_Proc,
entry_size: int,
entry_align: int,
key_offset: uintptr,
key_size: int,
value_offset: uintptr,
value_size: int,
}
INITIAL_HASH_SEED :: 0xcbf29ce484222325;
_fnv64a :: proc "contextless" (data: []byte, seed: u64 = INITIAL_HASH_SEED) -> u64 {
h: u64 = seed;
for b in data {
h = (h ~ u64(b)) * 0x100000001b3;
}
return h;
}
default_hash :: inline proc "contextless" (data: []byte) -> uintptr {
return uintptr(_fnv64a(data));
}
default_hash_string :: inline proc "contextless" (s: string) -> uintptr {
return default_hash(transmute([]byte)(s));
}
default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> uintptr {
s := Raw_Slice{data, size};
return default_hash(transmute([]byte)(s));
}
@(private)
_default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr where N <= 16 {
h := u64(seed) + 0xcbf29ce484222325;
p := uintptr(data);
inline for _ in 0..<N {
b := u64((^byte)(p)^);
h = (h ~ b) * 0x100000001b3;
p += 1;
}
return uintptr(h);
}
default_hasher_n :: inline proc "contextless" (data: rawptr, seed: uintptr, N: int) -> uintptr {
h := u64(seed) + 0xcbf29ce484222325;
p := uintptr(data);
for _ in 0..<N {
b := u64((^byte)(p)^);
h = (h ~ b) * 0x100000001b3;
p += 1;
}
return uintptr(h);
}
// NOTE(bill): There are loads of predefined ones to improve optimizations for small types
default_hasher1 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 1); }
default_hasher2 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 2); }
default_hasher3 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 3); }
default_hasher4 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 4); }
default_hasher5 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 5); }
default_hasher6 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 6); }
default_hasher7 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 7); }
default_hasher8 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 8); }
default_hasher9 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 9); }
default_hasher10 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 10); }
default_hasher11 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 11); }
default_hasher12 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 12); }
default_hasher13 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 13); }
default_hasher14 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 14); }
default_hasher15 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 15); }
default_hasher16 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 16); }
default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
h := u64(seed) + 0xcbf29ce484222325;
str := (^[]byte)(data)^;
for b in str {
h = (h ~ u64(b)) * 0x100000001b3;
}
return uintptr(h);
}
default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
h := u64(seed) + 0xcbf29ce484222325;
ptr := (^uintptr)(data)^;
for (^byte)(ptr)^ != 0 {
b := (^byte)(ptr)^;
h = (h ~ u64(b)) * 0x100000001b3;
ptr += 1;
}
return uintptr(h);
}
source_code_location_hash :: proc(s: Source_Code_Location) -> uintptr {
hash := _fnv64a(transmute([]byte)s.file_path);
hash = hash ~ (u64(s.line) * 0x100000001b3);
hash = hash ~ (u64(s.column) * 0x100000001b3);
return uintptr(hash);
}
__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
header := Map_Header{m = (^Raw_Map)(m)};
Entry :: struct {
hash: uintptr,
next: int,
key: K,
value: V,
};
header.equal = intrinsics.type_equal_proc(K);
header.entry_size = int(size_of(Entry));
header.entry_align = int(align_of(Entry));
header.key_offset = uintptr(offset_of(Entry, key));
header.key_size = int(size_of(K));
header.value_offset = uintptr(offset_of(Entry, value));
header.value_size = int(size_of(V));
return header;
}
__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
array := (^Raw_Slice)(array_);
if new_count < array.len {
return true;
}
assert(allocator.procedure != nil);
old_size := array.len*size_of(T);
new_size := new_count*size_of(T);
new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
if new_data == nil {
return false;
}
array.data = new_data;
array.len = new_count;
return true;
}
__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc);
old_len := len(m.hashes);
__slice_resize(&m.hashes, cap, m.entries.allocator, loc);
for i in old_len..<len(m.hashes) {
m.hashes[i] = -1;
}
}
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
new_header: Map_Header = header;
nm := Raw_Map{};
nm.entries.allocator = m.entries.allocator;
new_header.m = &nm;
c := context;
if m.entries.allocator.procedure != nil {
c.allocator = m.entries.allocator;
}
context = c;
new_count := new_count;
new_count = max(new_count, 2*m.entries.len);
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
__slice_resize(&nm.hashes, new_count, m.entries.allocator, loc);
for i in 0 ..< new_count {
nm.hashes[i] = -1;
}
for i in 0 ..< m.entries.len {
if len(nm.hashes) == 0 {
__dynamic_map_grow(new_header, loc);
}
entry_header := __dynamic_map_get_entry(header, i);
entry_hash := __get_map_hash_from_entry(header, entry_header);
fr := __dynamic_map_find(new_header, entry_hash);
j := __dynamic_map_add_entry(new_header, entry_hash, loc);
if fr.entry_prev < 0 {
nm.hashes[fr.hash_index] = j;
} else {
e := __dynamic_map_get_entry(new_header, fr.entry_prev);
e.next = j;
}
e := __dynamic_map_get_entry(new_header, j);
__dynamic_map_copy_entry(header, e, entry_header);
e.next = fr.entry_index;
if __dynamic_map_full(new_header) {
__dynamic_map_grow(new_header, loc);
}
}
delete(m.hashes, m.entries.allocator, loc);
free(m.entries.data, m.entries.allocator, loc);
header.m^ = nm;
}
__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr {
index := __dynamic_map_find(h, hash).entry_index;
if index >= 0 {
data := uintptr(__dynamic_map_get_entry(h, index));
return rawptr(data + h.value_offset);
}
return nil;
}
__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) #no_bounds_check {
index: int;
assert(value != nil);
if len(h.m.hashes) == 0 {
__dynamic_map_reserve(h, INITIAL_MAP_CAP, loc);
__dynamic_map_grow(h, loc);
}
fr := __dynamic_map_find(h, hash);
if fr.entry_index >= 0 {
index = fr.entry_index;
} else {
index = __dynamic_map_add_entry(h, hash, loc);
if fr.entry_prev >= 0 {
entry := __dynamic_map_get_entry(h, fr.entry_prev);
entry.next = index;
} else {
h.m.hashes[fr.hash_index] = index;
}
}
{
e := __dynamic_map_get_entry(h, index);
e.hash = hash.hash;
key := rawptr(uintptr(e) + h.key_offset);
mem_copy(key, hash.key_ptr, h.key_size);
val := rawptr(uintptr(e) + h.value_offset);
mem_copy(val, value, h.value_size);
}
if __dynamic_map_full(h) {
__dynamic_map_grow(h, loc);
}
}
__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) {
// TODO(bill): Determine an efficient growing rate
new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP);
__dynamic_map_rehash(h, new_count, loc);
}
__dynamic_map_full :: inline proc "contextless" (using h: Map_Header) -> bool {
return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
}
__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool {
if a.hash == b.hash {
return h.equal(a.key_ptr, b.key_ptr);
}
return false;
}
__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check {
fr := Map_Find_Result{-1, -1, -1};
if n := uintptr(len(m.hashes)); n > 0 {
fr.hash_index = int(hash.hash % n);
fr.entry_index = m.hashes[fr.hash_index];
for fr.entry_index >= 0 {
entry := __dynamic_map_get_entry(h, fr.entry_index);
entry_hash := __get_map_hash_from_entry(h, entry);
if __dynamic_map_hash_equal(h, entry_hash, hash) {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = entry.next;
}
}
return fr;
}
__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int {
prev := m.entries.len;
c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc);
if c != prev {
end := __dynamic_map_get_entry(h, c-1);
end.hash = hash.hash;
mem_copy(rawptr(uintptr(end) + key_offset), hash.key_ptr, key_size);
end.next = -1;
}
return prev;
}
__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) {
fr := __dynamic_map_find(h, hash);
if fr.entry_index >= 0 {
__dynamic_map_erase(h, fr);
}
}
__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header {
assert(0 <= index && index < m.entries.len);
return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size));
}
__dynamic_map_copy_entry :: proc "contextless" (h: Map_Header, new, old: ^Map_Entry_Header) {
mem_copy(new, old, h.entry_size);
}
__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
if fr.entry_prev < 0 {
m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
} else {
prev := __dynamic_map_get_entry(h, fr.entry_prev);
curr := __dynamic_map_get_entry(h, fr.entry_index);
prev.next = curr.next;
}
if (fr.entry_index == m.entries.len-1) {
// NOTE(bill): No need to do anything else, just pop
} else {
old := __dynamic_map_get_entry(h, fr.entry_index);
end := __dynamic_map_get_entry(h, m.entries.len-1);
__dynamic_map_copy_entry(h, old, end);
old_hash := __get_map_hash_from_entry(h, old);
if last := __dynamic_map_find(h, old_hash); last.entry_prev >= 0 {
last_entry := __dynamic_map_get_entry(h, last.entry_prev);
last_entry.next = fr.entry_index;
} else {
m.hashes[last.hash_index] = fr.entry_index;
}
}
m.entries.len -= 1;
}
+57 -4
View File
@@ -23,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
}
handle_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
context = default_context();
print_caller_location(Source_Code_Location{file, line, column, "", 0});
print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Index ");
print_i64(i64(index));
print_string(" is out of bounds range 0:");
@@ -36,7 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) -> ! {
context = default_context();
print_caller_location(Source_Code_Location{file, line, column, "", 0});
print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Invalid slice indices: ");
print_i64(i64(lo));
print_string(":");
@@ -67,7 +67,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: int,
}
handle_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
context = default_context();
print_caller_location(Source_Code_Location{file, line, column, "", 0});
print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Invalid dynamic array values: ");
print_i64(i64(low));
print_string(":");
@@ -87,7 +87,7 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
}
handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid) {
context = default_context();
print_caller_location(Source_Code_Location{file, line, column, "", 0});
print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Invalid type assertion from ");
print_typeid(from);
print_string(" to ");
@@ -98,6 +98,59 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
handle_error(file, line, column, from, to);
}
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: int, from, to: typeid, from_data: rawptr) {
if ok {
return;
}
variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
if id == nil || data == nil {
return id;
}
ti := type_info_base(type_info_of(id));
#partial switch v in ti.variant {
case Type_Info_Any:
return (^any)(data).id;
case Type_Info_Union:
tag_ptr := uintptr(data) + v.tag_offset;
idx := 0;
switch v.tag_type.size {
case 1: idx = int((^u8)(tag_ptr)^) - 1;
case 2: idx = int((^u16)(tag_ptr)^) - 1;
case 4: idx = int((^u32)(tag_ptr)^) - 1;
case 8: idx = int((^u64)(tag_ptr)^) - 1;
case 16: idx = int((^u128)(tag_ptr)^) - 1;
}
if idx < 0 {
return nil;
} else if idx < len(v.variants) {
return v.variants[idx].id;
}
}
return id;
}
handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid, from_data: rawptr) {
context = default_context();
actual := variant_type(from, from_data);
print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Invalid type assertion from ");
print_typeid(from);
print_string(" to ");
print_typeid(to);
if actual != from {
print_string(", actual type: ");
print_typeid(actual);
}
print_byte('\n');
type_assertion_trap();
}
handle_error(file, line, column, from, to, from_data);
}
make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) {
if 0 <= len {
return;
+24 -17
View File
@@ -93,18 +93,18 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
when ODIN_USE_LLVM_API {
when size_of(rawptr) == 8 {
@(link_name="llvm.memmove.p0i8.p0i8.i64")
llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memmove.p0i8.p0i8.i32")
llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
}
} else {
when size_of(rawptr) == 8 {
@(link_name="llvm.memmove.p0i8.p0i8.i64")
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memmove.p0i8.p0i8.i32")
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
}
}
}
@@ -121,18 +121,18 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
when ODIN_USE_LLVM_API {
when size_of(rawptr) == 8 {
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
}
} else {
when size_of(rawptr) == 8 {
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
}
}
}
@@ -180,9 +180,16 @@ mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int =
}
return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
}
memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
return memory_compare(a, b, n) == 0;
}
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
switch {
case a == b: return 0;
case a == nil: return -1;
case b == nil: return +1;
}
x := uintptr(a);
y := uintptr(b);
n := uintptr(n);
@@ -389,45 +396,45 @@ string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) {
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
}
@(default_calling_convention = "c")
@(default_calling_convention = "none")
foreign {
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
}
abs_f32 :: inline proc "contextless" (x: f32) -> f32 {
foreign {
@(link_name="llvm.fabs.f32") _abs :: proc "c" (x: f32) -> f32 ---
@(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 ---
}
return _abs(x);
}
abs_f64 :: inline proc "contextless" (x: f64) -> f64 {
foreign {
@(link_name="llvm.fabs.f64") _abs :: proc "c" (x: f64) -> f64 ---
@(link_name="llvm.fabs.f64") _abs :: proc "none" (x: f64) -> f64 ---
}
return _abs(x);
}
min_f32 :: proc(a, b: f32) -> f32 {
foreign {
@(link_name="llvm.minnum.f32") _min :: proc "c" (a, b: f32) -> f32 ---
@(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 ---
}
return _min(a, b);
}
min_f64 :: proc(a, b: f64) -> f64 {
foreign {
@(link_name="llvm.minnum.f64") _min :: proc "c" (a, b: f64) -> f64 ---
@(link_name="llvm.minnum.f64") _min :: proc "none" (a, b: f64) -> f64 ---
}
return _min(a, b);
}
max_f32 :: proc(a, b: f32) -> f32 {
foreign {
@(link_name="llvm.maxnum.f32") _max :: proc "c" (a, b: f32) -> f32 ---
@(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 ---
}
return _max(a, b);
}
max_f64 :: proc(a, b: f64) -> f64 {
foreign {
@(link_name="llvm.maxnum.f64") _max :: proc "c" (a, b: f64) -> f64 ---
@(link_name="llvm.maxnum.f64") _max :: proc "none" (a, b: f64) -> f64 ---
}
return _max(a, b);
}
+135
View File
@@ -0,0 +1,135 @@
package runtime
@(link_name="__umodti3")
umodti3 :: proc "c" (a, b: u128) -> u128 {
r: u128 = ---;
_ = udivmod128(a, b, &r);
return r;
}
@(link_name="__udivmodti4")
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
return udivmod128(a, b, rem);
}
@(link_name="__udivti3")
udivti3 :: proc "c" (a, b: u128) -> u128 {
return udivmodti4(a, b, nil);
}
@(link_name="__modti3")
modti3 :: proc "c" (a, b: i128) -> i128 {
s_a := a >> (128 - 1);
s_b := b >> (128 - 1);
an := (a ~ s_a) - s_a;
bn := (b ~ s_b) - s_b;
r: u128 = ---;
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
return (transmute(i128)r ~ s_a) - s_a;
}
@(link_name="__divmodti4")
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
return transmute(i128)u;
}
@(link_name="__divti3")
divti3 :: proc "c" (a, b: i128) -> i128 {
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
return transmute(i128)u;
}
@(link_name="__fixdfti")
fixdfti :: proc(a: u64) -> i128 {
significandBits :: 52;
typeWidth :: (size_of(u64)*8);
exponentBits :: (typeWidth - significandBits - 1);
maxExponent :: ((1 << exponentBits) - 1);
exponentBias :: (maxExponent >> 1);
implicitBit :: (u64(1) << significandBits);
significandMask :: (implicitBit - 1);
signBit :: (u64(1) << (significandBits + exponentBits));
absMask :: (signBit - 1);
exponentMask :: (absMask ~ significandMask);
// Break a into sign, exponent, significand
aRep := a;
aAbs := aRep & absMask;
sign := i128(-1 if aRep & signBit != 0 else 1);
exponent := u64((aAbs >> significandBits) - exponentBias);
significand := u64((aAbs & significandMask) | implicitBit);
// If exponent is negative, the result is zero.
if exponent < 0 {
return 0;
}
// If the value is too large for the integer type, saturate.
if exponent >= size_of(i128) * 8 {
return max(i128) if sign == 1 else min(i128);
}
// If 0 <= exponent < significandBits, right shift to get the result.
// Otherwise, shift left.
if exponent < significandBits {
return sign * i128(significand >> (significandBits - exponent));
} else {
return sign * (i128(significand) << (exponent - significandBits));
}
}
@(default_calling_convention = "none")
foreign {
@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
}
@(link_name="__floattidf")
floattidf :: proc(a: i128) -> f64 {
DBL_MANT_DIG :: 53;
if a == 0 {
return 0.0;
}
a := a;
N :: size_of(i128) * 8;
s := a >> (N-1);
a = (a ~ s) - s;
sd: = N - _clz_i128(a); // number of significant digits
e := u32(sd - 1); // exponent
if sd > DBL_MANT_DIG {
switch sd {
case DBL_MANT_DIG + 1:
a <<= 1;
case DBL_MANT_DIG + 2:
// okay
case:
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
};
a |= i128((a & 4) != 0);
a += 1;
a >>= 2;
if a & (1 << DBL_MANT_DIG) != 0 {
a >>= 1;
e += 1;
}
} else {
a <<= u128(DBL_MANT_DIG - sd);
}
fb: [2]u32;
fb[1] = (u32(s) & 0x80000000) | // sign
((e + 1023) << 20) | // exponent
((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
fb[1] = u32(a); // mantissa-low
return transmute(f64)fb;
}
+84 -84
View File
@@ -2,134 +2,134 @@ package runtime
@(link_name="__umodti3")
umodti3 :: proc "c" (a, b: u128) -> u128 {
r: u128 = ---;
_ = udivmod128(a, b, &r);
return r;
r: u128 = ---;
_ = udivmod128(a, b, &r);
return r;
}
@(link_name="__udivmodti4")
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
return udivmod128(a, b, rem);
return udivmod128(a, b, rem);
}
@(link_name="__udivti3")
udivti3 :: proc "c" (a, b: u128) -> u128 {
return udivmodti4(a, b, nil);
return udivmodti4(a, b, nil);
}
@(link_name="__modti3")
modti3 :: proc "c" (a, b: i128) -> i128 {
s_a := a >> (128 - 1);
s_b := b >> (128 - 1);
an := (a ~ s_a) - s_a;
bn := (b ~ s_b) - s_b;
s_a := a >> (128 - 1);
s_b := b >> (128 - 1);
an := (a ~ s_a) - s_a;
bn := (b ~ s_b) - s_b;
r: u128 = ---;
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
return (transmute(i128)r ~ s_a) - s_a;
r: u128 = ---;
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
return (transmute(i128)r ~ s_a) - s_a;
}
@(link_name="__divmodti4")
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
return transmute(i128)u;
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
return transmute(i128)u;
}
@(link_name="__divti3")
divti3 :: proc "c" (a, b: i128) -> i128 {
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
return transmute(i128)u;
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
return transmute(i128)u;
}
@(link_name="__fixdfti")
fixdfti :: proc(a: u64) -> i128 {
significandBits :: 52;
typeWidth :: (size_of(u64)*8);
exponentBits :: (typeWidth - significandBits - 1);
maxExponent :: ((1 << exponentBits) - 1);
exponentBias :: (maxExponent >> 1);
significandBits :: 52;
typeWidth :: (size_of(u64)*8);
exponentBits :: (typeWidth - significandBits - 1);
maxExponent :: ((1 << exponentBits) - 1);
exponentBias :: (maxExponent >> 1);
implicitBit :: (u64(1) << significandBits);
significandMask :: (implicitBit - 1);
signBit :: (u64(1) << (significandBits + exponentBits));
absMask :: (signBit - 1);
exponentMask :: (absMask ~ significandMask);
implicitBit :: (u64(1) << significandBits);
significandMask :: (implicitBit - 1);
signBit :: (u64(1) << (significandBits + exponentBits));
absMask :: (signBit - 1);
exponentMask :: (absMask ~ significandMask);
// Break a into sign, exponent, significand
aRep := a;
aAbs := aRep & absMask;
sign := i128(-1 if aRep & signBit != 0 else 1);
exponent := u64((aAbs >> significandBits) - exponentBias);
significand := u64((aAbs & significandMask) | implicitBit);
// Break a into sign, exponent, significand
aRep := a;
aAbs := aRep & absMask;
sign := i128(-1 if aRep & signBit != 0 else 1);
exponent := u64((aAbs >> significandBits) - exponentBias);
significand := u64((aAbs & significandMask) | implicitBit);
// If exponent is negative, the result is zero.
if exponent < 0 {
return 0;
}
// If exponent is negative, the result is zero.
if exponent < 0 {
return 0;
}
// If the value is too large for the integer type, saturate.
if exponent >= size_of(i128) * 8 {
return max(i128) if sign == 1 else min(i128);
}
// If the value is too large for the integer type, saturate.
if exponent >= size_of(i128) * 8 {
return max(i128) if sign == 1 else min(i128);
}
// If 0 <= exponent < significandBits, right shift to get the result.
// Otherwise, shift left.
if exponent < significandBits {
return sign * i128(significand >> (significandBits - exponent));
} else {
return sign * (i128(significand) << (exponent - significandBits));
}
// If 0 <= exponent < significandBits, right shift to get the result.
// Otherwise, shift left.
if exponent < significandBits {
return sign * i128(significand >> (significandBits - exponent));
} else {
return sign * (i128(significand) << (exponent - significandBits));
}
}
@(default_calling_convention = "none")
foreign {
@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
}
@(link_name="__floattidf")
floattidf :: proc(a: i128) -> f64 {
DBL_MANT_DIG :: 53;
if a == 0 {
return 0.0;
}
a := a;
N :: size_of(i128) * 8;
s := a >> (N-1);
a = (a ~ s) - s;
sd: = N - _clz_i128(a); // number of significant digits
e := u32(sd - 1); // exponent
if sd > DBL_MANT_DIG {
switch sd {
case DBL_MANT_DIG + 1:
a <<= 1;
case DBL_MANT_DIG + 2:
// okay
case:
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
};
DBL_MANT_DIG :: 53;
if a == 0 {
return 0.0;
}
a := a;
N :: size_of(i128) * 8;
s := a >> (N-1);
a = (a ~ s) - s;
sd: = N - _clz_i128(a); // number of significant digits
e := u32(sd - 1); // exponent
if sd > DBL_MANT_DIG {
switch sd {
case DBL_MANT_DIG + 1:
a <<= 1;
case DBL_MANT_DIG + 2:
// okay
case:
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
};
a |= i128((a & 4) != 0);
a += 1;
a >>= 2;
a |= i128((a & 4) != 0);
a += 1;
a >>= 2;
if a & (1 << DBL_MANT_DIG) != 0 {
a >>= 1;
e += 1;
}
} else {
a <<= u128(DBL_MANT_DIG - sd);
}
fb: [2]u32;
fb[1] = (u32(s) & 0x80000000) | // sign
((e + 1023) << 20) | // exponent
((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
fb[1] = u32(a); // mantissa-low
return transmute(f64)fb;
if a & (1 << DBL_MANT_DIG) != 0 {
a >>= 1;
e += 1;
}
} else {
a <<= u128(DBL_MANT_DIG - sd);
}
fb: [2]u32;
fb[1] = (u32(s) & 0x80000000) | // sign
((e + 1023) << 20) | // exponent
((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
fb[1] = u32(a); // mantissa-low
return transmute(f64)fb;
}
+1 -1
View File
@@ -350,7 +350,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
print_byte(']');
case Type_Info_Opaque:
print_string("opaque ");
print_string("#opaque ");
print_type(info.elem);
case Type_Info_Simd_Vector:
+5 -6
View File
@@ -2,15 +2,14 @@ package runtime
foreign import kernel32 "system:Kernel32.lib"
windows_trap_array_bounds :: proc "contextless" () -> ! {
DWORD :: u32;
ULONG_PTR :: uint;
@(private)
foreign kernel32 {
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
}
windows_trap_array_bounds :: proc "contextless" () -> ! {
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C;
foreign kernel32 {
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
}
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil);
}
+42 -1
View File
@@ -216,7 +216,7 @@ split_last :: proc(array: $T/[]$E) -> (rest: T, last: E) {
first :: proc(array: $T/[]$E) -> E {
return array[0];
}
last :: proc(array: $T/[]$E) -> ^E {
last :: proc(array: $T/[]$E) -> E {
return array[len(array)-1];
}
@@ -252,3 +252,44 @@ get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
as_ptr :: proc(array: $T/[]$E) -> ^E {
return raw_data(array);
}
mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> []V {
r := make([]V, len(s), allocator);
for v, i in s {
r[i] = f(v);
}
return r;
}
reduce :: proc(s: $S/[]$U, initializer: $V, f: proc(V, U) -> V) -> V {
r := initializer;
for v in s {
r = f(r, v);
}
return r;
}
filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -> S {
r := make([dynamic]S, 0, 0, allocator);
for v in s {
if f(v) {
append(&r, v);
}
}
return r[:];
}
dot_product :: proc(a, b: $S/[]$T) -> T
where intrinsics.type_is_numeric(T) {
if len(a) != len(b) {
panic("slice.dot_product: slices of unequal length");
}
r: T;
#no_bounds_check for _, i in a {
r += a[i] * b[i];
}
return r;
}
+4 -4
View File
@@ -54,17 +54,17 @@ reverse_sort :: proc(data: $T/[]$E) where ORD(E) {
// TODO(bill): Should `sort_by_key` exist or is `sort_by` more than enough?
sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
context.user_ptr = rawptr(key);
context._internal = rawptr(key);
sort_by(data, proc(i, j: E) -> bool {
k := (proc(E) -> K)(context.user_ptr);
k := (proc(E) -> K)(context._internal);
return k(i) < k(j);
});
}
reverse_sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
context.user_ptr = rawptr(key);
context._internal = rawptr(key);
sort_by(data, proc(i, j: E) -> bool {
k := (proc(E) -> K)(context.user_ptr);
k := (proc(E) -> K)(context._internal);
return k(j) < k(i);
});
}
+177 -82
View File
@@ -3,15 +3,12 @@ package strings
import "core:mem"
import "core:unicode/utf8"
import "core:strconv"
import "core:io"
Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool);
Builder :: struct {
buf: [dynamic]byte,
// The custom flush procedure allows for the ability to flush the buffer, i.e. write to file
flush_proc: Builder_Flush_Proc,
flush_data: rawptr,
}
make_builder_none :: proc(allocator := context.allocator) -> Builder {
@@ -32,6 +29,61 @@ make_builder :: proc{
make_builder_len_cap,
};
init_builder_none :: proc(b: ^Builder, allocator := context.allocator) {
b.buf = make([dynamic]byte, allocator);
}
init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) {
b.buf = make([dynamic]byte, len, allocator);
}
init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) {
b.buf = make([dynamic]byte, len, cap, allocator);
}
init_builder :: proc{
init_builder_none,
init_builder_len,
init_builder_len_cap,
};
@(private)
_builder_stream_vtable := &io.Stream_VTable{
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Builder)(s.stream_data);
n = write_bytes(b, p);
if len(b.buf) == cap(b.buf) {
err = .EOF;
}
return;
},
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
b := (^Builder)(s.stream_data);
_ = write_byte(b, c);
if len(b.buf) == cap(b.buf) {
return .EOF;
}
return nil;
},
impl_size = proc(s: io.Stream) -> i64 {
b := (^Builder)(s.stream_data);
return i64(len(b.buf));
},
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Builder)(s.stream_data);
delete(b.buf);
return .None;
},
};
to_stream :: proc(b: ^Builder) -> io.Stream {
return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b};
}
to_writer :: proc(b: ^Builder) -> io.Writer {
w, _ := io.to_writer(to_stream(b));
return w;
}
@@ -48,24 +100,6 @@ reset_builder :: proc(b: ^Builder) {
clear(&b.buf);
}
flush_builder :: proc(b: ^Builder) -> (was_reset: bool) {
if b.flush_proc != nil {
was_reset = b.flush_proc(b);
if was_reset {
reset_builder(b);
}
}
return;
}
flush_builder_check_space :: proc(b: ^Builder, required: int) -> (was_reset: bool) {
if n := max(cap(b.buf) - len(b.buf), 0); n < required {
was_reset = flush_builder(b);
}
return;
}
builder_from_slice :: proc(backing: []byte) -> Builder {
s := transmute(mem.Raw_Slice)backing;
@@ -94,7 +128,6 @@ builder_space :: proc(b: Builder) -> int {
}
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
flush_builder_check_space(b, 1);
if builder_space(b^) > 0 {
append(&b.buf, x);
n += 1;
@@ -105,7 +138,6 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
x := x;
for len(x) != 0 {
flush_builder_check_space(b, len(x));
space := builder_space(b^);
if space == 0 {
break; // No need to append
@@ -121,20 +153,56 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
return;
}
write_rune :: proc(b: ^Builder, r: rune) -> int {
if r < utf8.RUNE_SELF {
return write_byte(b, byte(r));
write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
return io.write_rune(to_writer(b), r);
}
write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) {
return write_quoted_rune(to_writer(b), r);
}
@(private)
_write_byte :: proc(w: io.Writer, c: byte) -> int {
err := io.write_byte(w, c);
return 1 if err == nil else 0;
}
write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
quote := byte('\'');
n += _write_byte(w, quote);
buf, width := utf8.encode_rune(r);
if width == 1 && r == utf8.RUNE_ERROR {
n += _write_byte(w, '\\');
n += _write_byte(w, 'x');
n += _write_byte(w, DIGITS_LOWER[buf[0]>>4]);
n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf]);
} else {
n += write_escaped_rune(w, r, quote);
}
s, n := utf8.encode_rune(r);
write_bytes(b, s[:n]);
return n;
n += _write_byte(w, quote);
return;
}
write_string :: proc(b: ^Builder, s: string) -> (n: int) {
return write_bytes(b, transmute([]byte)s);
write_string :: proc{
write_string_builder,
write_string_writer,
};
write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) {
return write_string_writer(to_writer(b), s);
}
write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
n, _ = io.write(w, transmute([]byte)s);
return;
}
pop_byte :: proc(b: ^Builder) -> (r: byte) {
if len(b.buf) == 0 {
return 0;
@@ -156,8 +224,17 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
@(private, static)
DIGITS_LOWER := "0123456789abcdefx";
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
n += write_byte(b, quote);
write_quoted_string :: proc{
write_quoted_string_builder,
write_quoted_string_writer,
};
write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
return write_quoted_string_writer(to_writer(b), str, quote);
}
write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) {
n += _write_byte(w, quote);
for width, s := 0, str; len(s) > 0; s = s[width:] {
r := rune(s[0]);
width = 1;
@@ -165,57 +242,75 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n:
r, width = utf8.decode_rune_in_string(s);
}
if width == 1 && r == utf8.RUNE_ERROR {
n += write_byte(b, '\\');
n += write_byte(b, 'x');
n += write_byte(b, DIGITS_LOWER[s[0]>>4]);
n += write_byte(b, DIGITS_LOWER[s[0]&0xf]);
n += _write_byte(w, '\\');
n += _write_byte(w, 'x');
n += _write_byte(w, DIGITS_LOWER[s[0]>>4]);
n += _write_byte(w, DIGITS_LOWER[s[0]&0xf]);
continue;
}
n += write_escaped_rune(b, r, quote);
n += write_escaped_rune(w, r, quote);
}
n += write_byte(b, quote);
n += _write_byte(w, quote);
return;
}
write_encoded_rune :: proc{
write_encoded_rune_builder,
write_encoded_rune_writer,
};
write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
return write_encoded_rune_writer(to_writer(b), r, write_quote);
}
write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) {
if write_quote {
n += write_byte(b, '\'');
n += _write_byte(w, '\'');
}
switch r {
case '\a': n += write_string(b, `\a"`);
case '\b': n += write_string(b, `\b"`);
case '\e': n += write_string(b, `\e"`);
case '\f': n += write_string(b, `\f"`);
case '\n': n += write_string(b, `\n"`);
case '\r': n += write_string(b, `\r"`);
case '\t': n += write_string(b, `\t"`);
case '\v': n += write_string(b, `\v"`);
case '\a': n += write_string(w, `\a"`);
case '\b': n += write_string(w, `\b"`);
case '\e': n += write_string(w, `\e"`);
case '\f': n += write_string(w, `\f"`);
case '\n': n += write_string(w, `\n"`);
case '\r': n += write_string(w, `\r"`);
case '\t': n += write_string(w, `\t"`);
case '\v': n += write_string(w, `\v"`);
case:
if r < 32 {
n += write_string(b, `\x`);
n += write_string(w, `\x`);
buf: [2]byte;
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil);
switch len(s) {
case 0: n += write_string(b, "00");
case 1: n += write_byte(b, '0');
case 2: n += write_string(b, s);
case 0: n += write_string(w, "00");
case 1: n += _write_byte(w, '0');
case 2: n += write_string(w, s);
}
} else {
n += write_rune(b, r);
rn, _ := io.write_rune(w, r);
n += rn;
}
}
if write_quote {
n += write_byte(b, '\'');
n += _write_byte(w, '\'');
}
return;
}
write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
write_escaped_rune :: proc{
write_escaped_rune_builder,
write_escaped_rune_writer,
};
write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
return write_escaped_rune_writer(to_writer(b), r, quote, html_safe);
}
write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) {
is_printable :: proc(r: rune) -> bool {
if r <= 0xff {
switch r {
@@ -233,54 +328,54 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
if html_safe {
switch r {
case '<', '>', '&':
n += write_byte(b, '\\');
n += write_byte(b, 'u');
n += _write_byte(w, '\\');
n += _write_byte(w, 'u');
for s := 12; s >= 0; s -= 4 {
n += write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]);
n += _write_byte(w, DIGITS_LOWER[r>>uint(s) & 0xf]);
}
return;
}
}
if r == rune(quote) || r == '\\' {
n += write_byte(b, '\\');
n += write_byte(b, byte(r));
n += _write_byte(w, '\\');
n += _write_byte(w, byte(r));
return;
} else if is_printable(r) {
n += write_encoded_rune(b, r, false);
n += write_encoded_rune(w, r, false);
return;
}
switch r {
case '\a': n += write_string(b, `\a`);
case '\b': n += write_string(b, `\b`);
case '\e': n += write_string(b, `\e`);
case '\f': n += write_string(b, `\f`);
case '\n': n += write_string(b, `\n`);
case '\r': n += write_string(b, `\r`);
case '\t': n += write_string(b, `\t`);
case '\v': n += write_string(b, `\v`);
case '\a': n += write_string(w, `\a`);
case '\b': n += write_string(w, `\b`);
case '\e': n += write_string(w, `\e`);
case '\f': n += write_string(w, `\f`);
case '\n': n += write_string(w, `\n`);
case '\r': n += write_string(w, `\r`);
case '\t': n += write_string(w, `\t`);
case '\v': n += write_string(w, `\v`);
case:
switch c := r; {
case c < ' ':
n += write_byte(b, '\\');
n += write_byte(b, 'x');
n += write_byte(b, DIGITS_LOWER[byte(c)>>4]);
n += write_byte(b, DIGITS_LOWER[byte(c)&0xf]);
n += _write_byte(w, '\\');
n += _write_byte(w, 'x');
n += _write_byte(w, DIGITS_LOWER[byte(c)>>4]);
n += _write_byte(w, DIGITS_LOWER[byte(c)&0xf]);
case c > utf8.MAX_RUNE:
c = 0xfffd;
fallthrough;
case c < 0x10000:
n += write_byte(b, '\\');
n += write_byte(b, 'u');
n += _write_byte(w, '\\');
n += _write_byte(w, 'u');
for s := 12; s >= 0; s -= 4 {
n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]);
}
case:
n += write_byte(b, '\\');
n += write_byte(b, 'U');
n += _write_byte(w, '\\');
n += _write_byte(w, 'U');
for s := 28; s >= 0; s -= 4 {
n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]);
}
}
}
+269
View File
@@ -0,0 +1,269 @@
package strings
import "core:io"
import "core:unicode"
import "core:unicode/utf8"
to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
if len(s) == 0 {
return "";
}
b: Builder;
init_builder(&b, 0, 0, allocator);
s := s;
for c, i in s {
if c != utf8.RUNE_ERROR {
continue;
}
_, w := utf8.decode_rune_in_string(s[i:]);
if w == 1 {
grow_builder(&b, len(s) + len(replacement));
write_string(&b, s[:i]);
s = s[i:];
break;
}
}
if builder_cap(b) == 0 {
return clone(s, allocator);
}
invalid := false;
for i := 0; i < len(s); /**/ {
c := s[i];
if c < utf8.RUNE_SELF {
i += 1;
invalid = false;
write_byte(&b, c);
continue;
}
_, w := utf8.decode_rune_in_string(s[i:]);
if w == 1 {
i += 1;
if !invalid {
invalid = true;
write_string(&b, replacement);
}
continue;
}
invalid = false;
write_string(&b, s[i:][:w]);
i += w;
}
return to_string(b);
}
to_lower :: proc(s: string, allocator := context.allocator) -> string {
b: Builder;
init_builder(&b, 0, len(s), allocator);
for r in s {
write_rune_builder(&b, unicode.to_lower(r));
}
return to_string(b);
}
to_upper :: proc(s: string, allocator := context.allocator) -> string {
b: Builder;
init_builder(&b, 0, len(s), allocator);
for r in s {
write_rune_builder(&b, unicode.to_upper(r));
}
return to_string(b);
}
is_delimiter :: proc(c: rune) -> bool {
return c == '-' || c == '_' || is_space(c);
}
is_separator :: proc(r: rune) -> bool {
if r <= 0x7f {
switch r {
case '0'..'9': return false;
case 'a'..'z': return false;
case 'A'..'Z': return false;
case '_': return false;
}
return true;
}
// TODO(bill): unicode categories
// if unicode.is_letter(r) || unicode.is_digit(r) {
// return false;
// }
return unicode.is_space(r);
}
string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) {
prev, curr: rune;
for next in s {
if curr == 0 {
prev = curr;
curr = next;
continue;
}
callback(w, prev, curr, next);
prev = curr;
curr = next;
}
if len(s) > 0 {
callback(w, prev, curr, 0);
}
}
to_lower_camel_case :: to_camel_case;
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
s := s;
s = trim_space(s);
b: Builder;
init_builder(&b, 0, len(s), allocator);
w := to_writer(&b);
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
if !is_delimiter(curr) {
if is_delimiter(prev) {
io.write_rune(w, unicode.to_upper(curr));
} else if unicode.is_lower(prev) {
io.write_rune(w, curr);
} else {
io.write_rune(w, unicode.to_lower(curr));
}
}
});
return to_string(b);
}
to_upper_camel_case :: to_pascal_case;
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
s := s;
s = trim_space(s);
b: Builder;
init_builder(&b, 0, len(s), allocator);
w := to_writer(&b);
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
if !is_delimiter(curr) {
if is_delimiter(prev) || prev == 0 {
io.write_rune(w, unicode.to_upper(curr));
} else if unicode.is_lower(prev) {
io.write_rune(w, curr);
} else {
io.write_rune(w, unicode.to_lower(curr));
}
}
});
return to_string(b);
}
to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
s := s;
s = trim_space(s);
b: Builder;
init_builder(&b, 0, len(s), allocator);
w := to_writer(&b);
adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower;
prev, curr: rune;
for next in s {
if is_delimiter(curr) {
if !is_delimiter(prev) {
io.write_rune(w, delimiter);
}
} else if unicode.is_upper(curr) {
if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
io.write_rune(w, delimiter);
}
io.write_rune(w, adjust_case(curr));
} else if curr != 0 {
io.write_rune(w, adjust_case(curr));
}
prev = curr;
curr = next;
}
if len(s) > 0 {
if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
io.write_rune(w, delimiter);
}
io.write_rune(w, adjust_case(curr));
}
return to_string(b);
}
to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', false, allocator);
}
to_screaming_snake_case :: to_upper_snake_case;
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', true, allocator);
}
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', false, allocator);
}
to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', true, allocator);
}
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
delimiter :: '_';
s := s;
s = trim_space(s);
b: Builder;
init_builder(&b, 0, len(s), allocator);
w := to_writer(&b);
prev, curr: rune;
for next in s {
if is_delimiter(curr) {
if !is_delimiter(prev) {
io.write_rune(w, delimiter);
}
} else if unicode.is_upper(curr) {
if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
io.write_rune(w, delimiter);
}
io.write_rune(w, unicode.to_upper(curr));
} else if curr != 0 {
io.write_rune(w, unicode.to_lower(curr));
}
prev = curr;
curr = next;
}
if len(s) > 0 {
if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
io.write_rune(w, delimiter);
io.write_rune(w, unicode.to_upper(curr));
} else {
io.write_rune(w, unicode.to_lower(curr));
}
}
return to_string(b);
}
+177
View File
@@ -0,0 +1,177 @@
package strings
import "core:io"
import "core:unicode/utf8"
Reader :: struct {
s: string, // read-only buffer
i: i64, // current reading index
prev_rune: int, // previous reading index of rune or < 0
}
reader_init :: proc(r: ^Reader, s: string) {
r.s = s;
r.i = 0;
r.prev_rune = -1;
}
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
s.stream_data = r;
s.stream_vtable = _reader_vtable;
return;
}
reader_length :: proc(r: ^Reader) -> int {
if r.i >= i64(len(r.s)) {
return 0;
}
return int(i64(len(r.s)) - r.i);
}
reader_size :: proc(r: ^Reader) -> i64 {
return i64(len(r.s));
}
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
return 0, .EOF;
}
r.prev_rune = -1;
n = copy(p, r.s[r.i:]);
r.i += i64(n);
return;
}
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
if off < 0 {
return 0, .Invalid_Offset;
}
if off >= i64(len(r.s)) {
return 0, .EOF;
}
n = copy(p, r.s[off:]);
if n < len(p) {
err = .EOF;
}
return;
}
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.prev_rune = -1;
if r.i >= i64(len(r.s)) {
return 0, .EOF;
}
b := r.s[r.i];
r.i += 1;
return b, nil;
}
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread;
}
r.prev_rune = -1;
r.i -= 1;
return nil;
}
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
r.prev_rune = -1;
return 0, 0, .EOF;
}
r.prev_rune = int(r.i);
if c := r.s[r.i]; c < utf8.RUNE_SELF {
r.i += 1;
return rune(c), 1, nil;
}
ch, size = utf8.decode_rune_in_string(r.s[r.i:]);
r.i += i64(size);
return;
}
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread;
}
if r.prev_rune < 0 {
return .Invalid_Unread;
}
r.i = i64(r.prev_rune);
r.prev_rune = -1;
return nil;
}
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r.prev_rune = -1;
abs: i64;
switch whence {
case .Start:
abs = offset;
case .Current:
abs = r.i + offset;
case .End:
abs = i64(len(r.s)) + offset;
case:
return 0, .Invalid_Whence;
}
if abs < 0 {
return 0, .Invalid_Offset;
}
r.i = abs;
return abs, nil;
}
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
r.prev_rune = -1;
if r.i >= i64(len(r.s)) {
return 0, nil;
}
s := r.s[r.i:];
m: int;
m, err = io.write_string(w, s);
if m > len(s) {
panic("bytes.Reader.write_to: invalid io.write_string count");
}
r.i += i64(m);
n = i64(m);
if m != len(s) && err == nil {
err = .Short_Write;
}
return;
}
@(private)
_reader_vtable := &io.Stream_VTable{
impl_size = proc(s: io.Stream) -> i64 {
r := (^Reader)(s.stream_data);
return reader_size(r);
},
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_read(r, p);
},
impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_read_at(r, p, off);
},
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
r := (^Reader)(s.stream_data);
return reader_read_byte(r);
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
r := (^Reader)(s.stream_data);
return reader_unread_byte(r);
},
impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_read_rune(r);
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
r := (^Reader)(s.stream_data);
return reader_unread_rune(r);
},
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r := (^Reader)(s.stream_data);
return reader_seek(r, offset, whence);
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_write_to(r, w);
},
};
+124 -278
View File
@@ -1,5 +1,6 @@
package strings
import "core:io"
import "core:mem"
import "core:unicode"
import "core:unicode/utf8"
@@ -225,7 +226,7 @@ index_byte :: proc(s: string, c: byte) -> int {
return -1;
}
// Returns i1 if c is not present
// Returns -1 if c is not present
last_index_byte :: proc(s: string, c: byte) -> int {
for i := len(s)-1; i >= 0; i -= 1 {
if s[i] == c {
@@ -467,10 +468,12 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
return;
}
@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
is_ascii_space :: proc(r: rune) -> bool {
switch r {
case '\t', '\n', '\v', '\f', '\r', ' ':
return true;
if r < utf8.RUNE_SELF {
return _ascii_space[u8(r)] != 0;
}
return false;
}
@@ -757,7 +760,8 @@ split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator
// Adjacent invalid bytes are only replaced once
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
str := s;
b := make_builder(0, len(str), allocator);
b: Builder;
init_builder(&b, 0, len(s), allocator);
has_error := false;
cursor := 0;
@@ -787,207 +791,6 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
}
to_lower :: proc(s: string, allocator := context.allocator) -> string {
b := make_builder(0, len(s), allocator);
for r in s {
write_rune(&b, unicode.to_lower(r));
}
return to_string(b);
}
to_upper :: proc(s: string, allocator := context.allocator) -> string {
b := make_builder(0, len(s), allocator);
for r in s {
write_rune(&b, unicode.to_upper(r));
}
return to_string(b);
}
is_delimiter :: proc(c: rune) -> bool {
return c == '-' || c == '_' || is_space(c);
}
is_separator :: proc(r: rune) -> bool {
if r <= 0x7f {
switch r {
case '0'..'9': return false;
case 'a'..'z': return false;
case 'A'..'Z': return false;
case '_': return false;
}
return true;
}
// TODO(bill): unicode categories
// if unicode.is_letter(r) || unicode.is_digit(r) {
// return false;
// }
return unicode.is_space(r);
}
string_case_iterator :: proc(b: ^Builder, s: string, callback: proc(b: ^Builder, prev, curr, next: rune)) {
prev, curr: rune;
for next in s {
if curr == 0 {
prev = curr;
curr = next;
continue;
}
callback(b, prev, curr, next);
prev = curr;
curr = next;
}
if len(s) > 0 {
callback(b, prev, curr, 0);
}
}
to_lower_camel_case :: to_camel_case;
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
s := s;
s = trim_space(s);
b := make_builder(0, len(s), allocator);
string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) {
if !is_delimiter(curr) {
if is_delimiter(prev) {
write_rune(b, unicode.to_upper(curr));
} else if unicode.is_lower(prev) {
write_rune(b, curr);
} else {
write_rune(b, unicode.to_lower(curr));
}
}
});
return to_string(b);
}
to_upper_camel_case :: to_pascal_case;
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
s := s;
s = trim_space(s);
b := make_builder(0, len(s), allocator);
string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) {
if !is_delimiter(curr) {
if is_delimiter(prev) || prev == 0 {
write_rune(b, unicode.to_upper(curr));
} else if unicode.is_lower(prev) {
write_rune(b, curr);
} else {
write_rune(b, unicode.to_lower(curr));
}
}
});
return to_string(b);
}
to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
s := s;
s = trim_space(s);
b := make_builder(0, len(s), allocator);
adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower;
prev, curr: rune;
for next in s {
if is_delimiter(curr) {
if !is_delimiter(prev) {
write_rune(&b, delimiter);
}
} else if unicode.is_upper(curr) {
if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
write_rune(&b, delimiter);
}
write_rune(&b, adjust_case(curr));
} else if curr != 0 {
write_rune(&b, adjust_case(curr));
}
prev = curr;
curr = next;
}
if len(s) > 0 {
if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
write_rune(&b, delimiter);
}
write_rune(&b, adjust_case(curr));
}
return to_string(b);
}
to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', false, allocator);
}
to_screaming_snake_case :: to_upper_snake_case;
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', true, allocator);
}
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', false, allocator);
}
to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', true, allocator);
}
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
delimiter :: '_';
s := s;
s = trim_space(s);
b := make_builder(0, len(s), allocator);
prev, curr: rune;
for next in s {
if is_delimiter(curr) {
if !is_delimiter(prev) {
write_rune(&b, delimiter);
}
} else if unicode.is_upper(curr) {
if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
write_rune(&b, delimiter);
}
write_rune(&b, unicode.to_upper(curr));
} else if curr != 0 {
write_rune(&b, unicode.to_lower(curr));
}
prev = curr;
curr = next;
}
if len(s) > 0 {
if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
write_rune(&b, delimiter);
write_rune(&b, unicode.to_upper(curr));
} else {
write_rune(&b, unicode.to_lower(curr));
}
}
return to_string(b);
}
reverse :: proc(s: string, allocator := context.allocator) -> string {
str := s;
n := len(str);
@@ -1013,7 +816,9 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
return "";
}
b := make_builder(allocator);
b: Builder;
init_builder(&b, allocator);
writer := to_writer(&b);
str := s;
column: int;
@@ -1024,7 +829,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
expand := tab_size - column%tab_size;
for i := 0; i < expand; i += 1 {
write_byte(&b, ' ');
io.write_byte(writer, ' ');
}
column += expand;
@@ -1035,7 +840,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
column += w;
}
write_rune(&b, r);
io.write_rune(writer, r);
}
str = str[w:];
@@ -1070,12 +875,15 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
remains := length-1;
pad_len := rune_count(pad);
b := make_builder(allocator);
b: Builder;
init_builder(&b, allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
write_pad_string(&b, pad, pad_len, remains/2);
write_string(&b, str);
write_pad_string(&b, pad, pad_len, (remains+1)/2);
w := to_writer(&b);
write_pad_string(w, pad, pad_len, remains/2);
io.write_string(w, str);
write_pad_string(w, pad, pad_len, (remains+1)/2);
return to_string(b);
}
@@ -1090,11 +898,14 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
remains := length-1;
pad_len := rune_count(pad);
b := make_builder(allocator);
b: Builder;
init_builder(&b, allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
write_string(&b, str);
write_pad_string(&b, pad, pad_len, remains);
w := to_writer(&b);
io.write_string(w, str);
write_pad_string(w, pad, pad_len, remains);
return to_string(b);
}
@@ -1109,86 +920,121 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
remains := length-1;
pad_len := rune_count(pad);
b := make_builder(allocator);
b: Builder;
init_builder(&b, allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
write_pad_string(&b, pad, pad_len, remains);
write_string(&b, str);
w := to_writer(&b);
write_pad_string(w, pad, pad_len, remains);
io.write_string(w, str);
return to_string(b);
}
to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
if len(s) == 0 {
return "";
}
b := make_builder_len_cap(0, 0, allocator);
s := s;
for c, i in s {
if c != utf8.RUNE_ERROR {
continue;
}
_, w := utf8.decode_rune_in_string(s[i:]);
if w == 1 {
grow_builder(&b, len(s) + len(replacement));
write_string(&b, s[:i]);
s = s[i:];
break;
}
}
if builder_cap(b) == 0 {
return clone(s, allocator);
}
invalid := false;
for i := 0; i < len(s); /**/ {
c := s[i];
if c < utf8.RUNE_SELF {
i += 1;
invalid = false;
write_byte(&b, c);
continue;
}
_, w := utf8.decode_rune_in_string(s[i:]);
if w == 1 {
i += 1;
if !invalid {
invalid = true;
write_string(&b, replacement);
}
continue;
}
invalid = false;
write_string(&b, s[i:][:w]);
i += w;
}
return to_string(b);
}
@private
write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) {
write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
repeats := remains / pad_len;
for i := 0; i < repeats; i += 1 {
write_string(b, pad);
io.write_string(w, pad);
}
n := remains % pad_len;
p := pad;
for i := 0; i < n; i += 1 {
r, w := utf8.decode_rune_in_string(p);
write_rune(b, r);
p = p[w:];
r, width := utf8.decode_rune_in_string(p);
io.write_rune(w, r);
p = p[width:];
}
}
// fields splits the string s around each instance of one or more consecutive white space character, defined by unicode.is_space
// returning a slice of substrings of s or an empty slice if s only contains white space
fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check {
n := 0;
was_space := 1;
set_bits := u8(0);
// check to see
for i in 0..<len(s) {
r := s[i];
set_bits |= r;
is_space := int(_ascii_space[r]);
n += was_space & ~is_space;
was_space = is_space;
}
if set_bits >= utf8.RUNE_SELF {
return fields_proc(s, unicode.is_space, allocator);
}
if n == 0 {
return nil;
}
a := make([]string, n, allocator);
na := 0;
field_start := 0;
i := 0;
for i < len(s) && _ascii_space[s[i]] != 0 {
i += 1;
}
field_start = i;
for i < len(s) {
if _ascii_space[s[i]] == 0 {
i += 1;
continue;
}
a[na] = s[field_start : i];
na += 1;
i += 1;
for i < len(s) && _ascii_space[s[i]] != 0 {
i += 1;
}
field_start = i;
}
if field_start < len(s) {
a[na] = s[field_start:];
}
return a;
}
// fields_proc splits the string s at each run of unicode code points `ch` satisfying f(ch)
// returns a slice of substrings of s
// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned
//
// fields_proc makes no guarantee about the order in which it calls f(ch)
// it assumes that `f` always returns the same value for a given ch
fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check {
substrings := make([dynamic]string, 0, 32, allocator);
start, end := -1, -1;
for r, offset in s {
end = offset;
if f(r) {
if start >= 0 {
append(&substrings, s[start : end]);
// -1 could be used, but just speed it up through bitwise not
// gotta love 2's complement
start = ~start;
}
} else {
if start < 0 {
start = end;
}
}
}
if start >= 0 {
append(&substrings, s[start : end]);
}
return substrings[:];
}
+70 -43
View File
@@ -2,60 +2,89 @@ package sync
import "core:mem"
import "core:time"
import "core:intrinsics"
import "intrinsics"
import "core:math/rand"
_, _ :: time, rand;
Channel_Direction :: enum i8 {
Both = 0,
Send = +1,
Recv = -1,
}
Channel :: struct(T: typeid) {
Channel :: struct(T: typeid, Direction := Channel_Direction.Both) {
using _internal: ^Raw_Channel,
}
channel_init :: proc(ch: ^$C/Channel($T), cap := 0, allocator := context.allocator) {
channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T)) {
channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_destroy :: proc(ch: $C/Channel($T)) {
channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_destroy :: proc(ch: $C/Channel($T, $D)) {
raw_channel_destroy(ch._internal);
}
channel_len :: proc(ch: $C/Channel($T)) -> int {
return ch._internal.len;
channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
res._internal = ch._internal;
return;
}
channel_cap :: proc(ch: $C/Channel($T)) -> int {
return ch._internal.cap;
channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
res._internal = ch._internal;
return;
}
channel_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) {
channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
return ch._internal.len if ch._internal != nil else 0;
}
channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
return ch._internal.cap if ch._internal != nil else 0;
}
channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
msg := msg;
_ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc);
}
channel_try_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) -> bool {
channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
msg := msg;
return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc);
}
channel_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T) {
channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
c := ch._internal;
if c == nil {
panic(message="cannot recv message; channel is nil", loc=loc);
}
mutex_lock(&c.mutex);
raw_channel_recv_impl(c, &msg, loc);
mutex_unlock(&c.mutex);
return;
}
channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T, ok: bool) {
channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal;
if mutex_try_lock(&c.mutex) {
if c != nil && mutex_try_lock(&c.mutex) {
if c.len > 0 {
raw_channel_recv_impl(c, &msg, loc);
ok = true;
@@ -64,7 +93,7 @@ channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T
}
return;
}
channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_location) -> (ok: bool) {
channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
res: T;
res, ok = channel_try_recv(ch, loc);
if ok && msg != nil {
@@ -74,32 +103,32 @@ channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_locatio
}
channel_is_nil :: proc(ch: $C/Channel($T)) -> bool {
channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
return ch._internal == nil;
}
channel_is_open :: proc(ch: $C/Channel($T)) -> bool {
channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
c := ch._internal;
return c != nil && !c.closed;
}
channel_eq :: proc(a, b: $C/Channel($T)) -> bool {
channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal == b._internal;
}
channel_ne :: proc(a, b: $C/Channel($T)) -> bool {
channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal != b._internal;
}
channel_can_send :: proc(ch: $C/Channel($T)) -> (ok: bool) {
channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
return raw_channel_can_send(ch._internal);
}
channel_can_recv :: proc(ch: $C/Channel($T)) -> (ok: bool) {
channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
return raw_channel_can_recv(ch._internal);
}
channel_peek :: proc(ch: $C/Channel($T)) -> int {
channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
c := ch._internal;
if c == nil {
return -1;
@@ -111,12 +140,12 @@ channel_peek :: proc(ch: $C/Channel($T)) -> int {
}
channel_close :: proc(ch: $C/Channel($T), loc := #caller_location) {
channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
raw_channel_close(ch._internal, loc);
}
channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) {
channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal;
if c == nil {
return;
@@ -127,12 +156,12 @@ channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) {
}
return;
}
channel_drain :: proc(ch: $C/Channel($T)) {
channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
raw_channel_drain(ch._internal);
}
channel_move :: proc(dst, src: $C/Channel($T)) {
channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
for msg in channel_iterator(src) {
channel_send(dst, msg);
}
@@ -258,18 +287,19 @@ raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc :=
for c.len >= c.cap {
condition_wait_for(&c.cond);
}
} else if c.len > 0 {
} else if c.len > 0 { // TODO(bill): determine correct behaviour
if !block {
return false;
}
condition_wait_for(&c.cond);
} else if c.len == 0 && !block {
return false;
}
send(c, msg);
condition_signal(&c.cond);
raw_channel_wait_queue_signal(c.recvq);
return true;
}
@@ -509,7 +539,7 @@ select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
return;
}
select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
@@ -535,7 +565,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
raw_channel_wait_queue_wait_on(&state);
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
@@ -560,7 +590,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
return;
}
select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
@@ -589,7 +619,7 @@ select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
raw_channel_wait_queue_wait_on(&state);
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
@@ -781,16 +811,15 @@ select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_che
return;
}
select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
index = 0;
index = -1;
return;
case 1:
if c := channels[0]; channel_can_recv(c) {
ok: bool;
if msg, ok = channel_try_recv(channels[0]); ok {
index = 0;
msg = channel_recv(c);
return;
}
return;
}
@@ -820,16 +849,14 @@ select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int)
return;
}
select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
index = -1;
switch len(channels) {
case 0:
index = 0;
return;
case 1:
if c := channels[0]; channel_can_send(c) {
if channel_try_send(channels[0], msg) {
index = 0;
channel_send(c, msg);
return;
}
return;
}
+35
View File
@@ -0,0 +1,35 @@
package sys_cpu
#assert(ODIN_USE_LLVM_API);
Cache_Line_Pad :: struct {_: [_cache_line_size]byte};
initialized: bool;
x86: struct {
_: Cache_Line_Pad,
has_aes: bool, // AES hardware implementation (AES NI)
has_adx: bool, // Multi-precision add-carry instruction extensions
has_avx: bool, // Advanced vector extension
has_avx2: bool, // Advanced vector extension 2
has_bmi1: bool, // Bit manipulation instruction set 1
has_bmi2: bool, // Bit manipulation instruction set 2
has_erms: bool, // Enhanced REP for MOVSB and STOSB
has_fma: bool, // Fused-multiply-add instructions
has_os_xsave: bool, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
has_pclmulqdq: bool, // PCLMULQDQ instruction - most often used for AES-GCM
has_popcnt: bool, // Hamming weight instruction POPCNT.
has_rdrand: bool, // RDRAND instruction (on-chip random number generator)
has_rdseed: bool, // RDSEED instruction (on-chip random number generator)
has_sse2: bool, // Streaming SIMD extension 2 (always available on amd64)
has_sse3: bool, // Streaming SIMD extension 3
has_ssse3: bool, // Supplemental streaming SIMD extension 3
has_sse41: bool, // Streaming SIMD extension 4 and 4.1
has_sse42: bool, // Streaming SIMD extension 4 and 4.2
_: Cache_Line_Pad,
};
init :: proc() {
_init();
}
+67
View File
@@ -0,0 +1,67 @@
//+build 386, amd64
package sys_cpu
_cache_line_size :: 64;
cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) {
return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} {
"cpuid",
"={ax},={bx},={cx},={dx},{ax},{cx}",
}(ax, cx));
}
xgetbv :: proc() -> (eax, edx: u32) {
return expand_to_tuple(asm(u32) -> struct{eax, edx: u32} {
"xgetbv",
"={ax},={dx},{cx}",
}(0));
}
_init :: proc() {
is_set :: proc(hwc: u32, value: u32) -> bool {
return hwc&value != 0;
}
initialized = true;
max_id, _, _, _ := cpuid(0, 0);
if max_id < 1 {
return;
}
_, _, ecx1, edx1 := cpuid(1, 0);
x86.has_sse2 = is_set(26, edx1);
x86.has_sse3 = is_set(0, ecx1);
x86.has_pclmulqdq = is_set(1, ecx1);
x86.has_ssse3 = is_set(9, ecx1);
x86.has_fma = is_set(12, ecx1);
x86.has_sse41 = is_set(19, ecx1);
x86.has_sse42 = is_set(20, ecx1);
x86.has_popcnt = is_set(23, ecx1);
x86.has_aes = is_set(25, ecx1);
x86.has_os_xsave = is_set(27, ecx1);
x86.has_rdrand = is_set(30, ecx1);
os_supports_avx := false;
if x86.has_os_xsave {
eax, _ := xgetbv();
os_supports_avx = is_set(1, eax) && is_set(2, eax);
}
x86.has_avx = is_set(28, ecx1) && os_supports_avx;
if max_id < 7 {
return;
}
_, ebx7, _, _ := cpuid(7, 0);
x86.has_bmi1 = is_set(3, ebx7);
x86.has_avx2 = is_set(5, ebx7) && os_supports_avx;
x86.has_bmi2 = is_set(8, ebx7);
x86.has_erms = is_set(9, ebx7);
x86.has_rdseed = is_set(18, ebx7);
x86.has_adx = is_set(19, ebx7);
}
+9 -9
View File
@@ -14,44 +14,44 @@ PTHREAD_ONCE_SIZE :: 8;
PTHREAD_RWLOCK_SIZE :: 192;
PTHREAD_RWLOCKATTR_SIZE :: 16;
pthread_t :: opaque u64;
pthread_t :: #opaque u64;
pthread_attr_t :: opaque struct #align 16 {
pthread_attr_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_ATTR_SIZE] c.char,
};
pthread_cond_t :: opaque struct #align 16 {
pthread_cond_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_COND_SIZE] c.char,
};
pthread_condattr_t :: opaque struct #align 16 {
pthread_condattr_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_CONDATTR_SIZE] c.char,
};
pthread_mutex_t :: opaque struct #align 16 {
pthread_mutex_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_MUTEX_SIZE] c.char,
};
pthread_mutexattr_t :: opaque struct #align 16 {
pthread_mutexattr_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_MUTEXATTR_SIZE] c.char,
};
pthread_once_t :: opaque struct #align 16 {
pthread_once_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_ONCE_SIZE] c.char,
};
pthread_rwlock_t :: opaque struct #align 16 {
pthread_rwlock_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_RWLOCK_SIZE] c.char,
};
pthread_rwlockattr_t :: opaque struct #align 16 {
pthread_rwlockattr_t :: #opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_RWLOCKATTR_SIZE] c.char,
};
+9 -9
View File
@@ -26,32 +26,32 @@ when size_of(int) == 8 {
PTHREAD_BARRIER_T_SIZE :: 20;
}
pthread_cond_t :: opaque struct #align 16 {
pthread_cond_t :: #opaque struct #align 16 {
_: [PTHREAD_COND_T_SIZE] c.char,
};
pthread_mutex_t :: opaque struct #align 16 {
pthread_mutex_t :: #opaque struct #align 16 {
_: [PTHREAD_MUTEX_T_SIZE] c.char,
};
pthread_rwlock_t :: opaque struct #align 16 {
pthread_rwlock_t :: #opaque struct #align 16 {
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
};
pthread_barrier_t :: opaque struct #align 16 {
pthread_barrier_t :: #opaque struct #align 16 {
_: [PTHREAD_BARRIER_T_SIZE] c.char,
};
pthread_attr_t :: opaque struct #align 16 {
pthread_attr_t :: #opaque struct #align 16 {
_: [PTHREAD_ATTR_T_SIZE] c.char,
};
pthread_condattr_t :: opaque struct #align 16 {
pthread_condattr_t :: #opaque struct #align 16 {
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
};
pthread_mutexattr_t :: opaque struct #align 16 {
pthread_mutexattr_t :: #opaque struct #align 16 {
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
};
pthread_rwlockattr_t :: opaque struct #align 16 {
pthread_rwlockattr_t :: #opaque struct #align 16 {
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
};
pthread_barrierattr_t :: opaque struct #align 16 {
pthread_barrierattr_t :: #opaque struct #align 16 {
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
};
+9 -9
View File
@@ -33,32 +33,32 @@ when size_of(int) == 8 {
PTHREAD_BARRIER_T_SIZE :: 20;
}
pthread_cond_t :: opaque struct #align 16 {
pthread_cond_t :: #opaque struct #align 16 {
_: [PTHREAD_COND_T_SIZE] c.char,
};
pthread_mutex_t :: opaque struct #align 16 {
pthread_mutex_t :: #opaque struct #align 16 {
_: [PTHREAD_MUTEX_T_SIZE] c.char,
};
pthread_rwlock_t :: opaque struct #align 16 {
pthread_rwlock_t :: #opaque struct #align 16 {
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
};
pthread_barrier_t :: opaque struct #align 16 {
pthread_barrier_t :: #opaque struct #align 16 {
_: [PTHREAD_BARRIER_T_SIZE] c.char,
};
pthread_attr_t :: opaque struct #align 16 {
pthread_attr_t :: #opaque struct #align 16 {
_: [PTHREAD_ATTR_T_SIZE] c.char,
};
pthread_condattr_t :: opaque struct #align 16 {
pthread_condattr_t :: #opaque struct #align 16 {
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
};
pthread_mutexattr_t :: opaque struct #align 16 {
pthread_mutexattr_t :: #opaque struct #align 16 {
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
};
pthread_rwlockattr_t :: opaque struct #align 16 {
pthread_rwlockattr_t :: #opaque struct #align 16 {
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
};
pthread_barrierattr_t :: opaque struct #align 16 {
pthread_barrierattr_t :: #opaque struct #align 16 {
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
};
+2 -2
View File
@@ -8,12 +8,12 @@ foreign kernel32 {
@(link_name="CreateProcessA") create_process_a :: proc(application_name, command_line: cstring,
process_attributes, thread_attributes: ^Security_Attributes,
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
current_direcotry: cstring, startup_info: ^Startup_Info,
current_directory: cstring, startup_info: ^Startup_Info,
process_information: ^Process_Information) -> Bool ---;
@(link_name="CreateProcessW") create_process_w :: proc(application_name, command_line: Wstring,
process_attributes, thread_attributes: ^Security_Attributes,
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
current_direcotry: Wstring, startup_info: ^Startup_Info,
current_directory: Wstring, startup_info: ^Startup_Info,
process_information: ^Process_Information) -> Bool ---;
@(link_name="GetExitCodeProcess") get_exit_code_process :: proc(process: Handle, exit: ^u32) -> Bool ---;
@(link_name="ExitProcess") exit_process :: proc(exit_code: u32) ---;
+585
View File
@@ -0,0 +1,585 @@
package text_scanner
import "core:fmt"
import "core:strings"
import "core:unicode"
import "core:unicode/utf8"
Position :: struct {
filename: string, // filename, if present
offset: int, // byte offset, starting @ 0
line: int, // line number, starting @ 1
column: int, // column number, starting @ 1 (character count per line)
}
position_is_valid :: proc(pos: Position) -> bool {
return pos.line > 0;
}
position_to_string :: proc(pos: Position, allocator := context.temp_allocator) -> string {
s := pos.filename;
if s == "" {
s = "<input>";
}
context.allocator = allocator;
if position_is_valid(pos) {
return fmt.aprintf("%s(%d:%d)", s, pos.line, pos.column);
} else {
return strings.clone(s);
}
}
EOF :: -1;
Ident :: -2;
Int :: -3;
Float :: -4;
Char :: -5;
String :: -6;
Raw_String :: -7;
Comment :: -8;
Scan_Flag :: enum u32 {
Scan_Idents,
Scan_Ints,
Scan_C_Int_Prefixes,
Scan_Floats,
Scan_Chars,
Scan_Strings,
Scan_Raw_Strings,
Scan_Comments,
Skip_Comments,
}
Scan_Flags :: bit_set[Scan_Flag; u32];
Odin_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments};
C_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_C_Int_Prefixes, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments};
Odin_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' ';
C_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<'\v' | 1<<'\f' | 1<<' ';
Scanner :: struct {
src: string,
src_pos: int,
src_end: int,
tok_pos: int,
tok_end: int,
ch: rune,
line: int,
column: int,
prev_line_len: int,
prev_char_len: int,
error: proc(s: ^Scanner, msg: string),
error_count: int,
flags: Scan_Flags,
whitespace: u64,
is_ident_rune: proc(ch: rune, i: int) -> bool,
pos: Position,
}
init :: proc(s: ^Scanner, src: string, filename := "") -> ^Scanner {
s^ = {};
s.src = src;
s.pos.filename = filename;
s.tok_pos = -1;
s.ch = -2; // no char read yet, not an EOF
s.line = 1;
s.flags = Odin_Like_Tokens;
s.whitespace = Odin_Whitespace;
return s;
}
@(private)
advance :: proc(s: ^Scanner) -> rune {
if s.src_pos >= len(s.src) {
s.prev_char_len = 0;
return EOF;
}
ch, width := rune(s.src[s.src_pos]), 1;
if ch >= utf8.RUNE_SELF {
ch, width = utf8.decode_rune_in_string(s.src[s.src_pos:]);
if ch == utf8.RUNE_ERROR && width == 1 {
s.src_pos += width;
s.prev_char_len = width;
s.column += 1;
error(s, "invalid UTF-8 encoding");
return ch;
}
}
s.src_pos += width;
s.prev_char_len = width;
s.column += 1;
switch ch {
case 0:
error(s, "invalid character NUL");
case '\n':
s.line += 1;
s.prev_line_len = s.column;
s.column = 0;
}
return ch;
}
next :: proc(s: ^Scanner) -> rune {
s.tok_pos = -1;
s.pos.line = 0;
ch := peek(s);
if ch != EOF {
s.ch = advance(s);
}
return ch;
}
peek :: proc(s: ^Scanner) -> rune {
if s.ch == -2 {
s.ch = advance(s);
if s.ch == '\ufeff' { // Ignore BOM
s.ch = advance(s);
}
}
return s.ch;
}
error :: proc(s: ^Scanner, msg: string) {
s.error_count += 1;
if s.error != nil {
s.error(s, msg);
return;
}
p := s.pos;
if !position_is_valid(p) {
p = position(s);
}
s := p.filename;
if s == "" {
s = "<input>";
}
if position_is_valid(p) {
fmt.eprintf("%s(%d:%d): %s\n", s, p.line, p.column, msg);
} else {
fmt.eprintf("%s: %s\n", s, msg);
}
}
errorf :: proc(s: ^Scanner, format: string, args: ..any) {
error(s, fmt.tprintf(format, ..args));
}
@(private)
is_ident_rune :: proc(s: ^Scanner, ch: rune, i: int) -> bool {
if s.is_ident_rune != nil {
return s.is_ident_rune(ch, i);
}
return ch == '_' || unicode.is_letter(ch) || unicode.is_digit(ch) && i > 0;
}
@(private)
scan_identifier :: proc(s: ^Scanner) -> rune {
ch := advance(s);
for i := 1; is_ident_rune(s, ch, i); i += 1 {
ch = advance(s);
}
return ch;
}
@(private) lower :: proc(ch: rune) -> rune { return ('a' - 'A') | ch; }
@(private) is_decimal :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9'; }
@(private) is_hex :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f'; }
@(private)
scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) {
lit_name :: proc(prefix: rune) -> string {
switch prefix {
case 'b': return "binary literal";
case 'o': return "octal literal";
case 'z': return "dozenal literal";
case 'x': return "hexadecimal literal";
}
return "decimal literal";
}
digits :: proc(s: ^Scanner, ch0: rune, base: int, invalid: ^rune) -> (ch: rune, digsep: int) {
ch = ch0;
if base <= 10 {
max := rune('0' + base);
for is_decimal(ch) || ch == '_' {
ds := 1;
if ch == '_' {
ds = 2;
} else if ch >= max && invalid^ == 0 {
invalid^ = ch;
}
digsep |= ds;
ch = advance(s);
}
} else {
for is_hex(ch) || ch == '_' {
ds := 1;
if ch == '_' {
ds = 2;
}
digsep |= ds;
ch = advance(s);
}
}
return;
}
ch, seen_dot := ch, seen_dot;
base := 10;
prefix := rune(0);
digsep := 0;
invalid := rune(0);
tok: rune;
ds: int;
if !seen_dot {
tok = Int;
if ch == '0' {
ch = advance(s);
p := lower(ch);
if .Scan_C_Int_Prefixes in s.flags {
switch p {
case 'b':
ch = advance(s);
base, prefix = 2, 'b';
case 'x':
ch = advance(s);
base, prefix = 16, 'x';
case:
base, prefix = 8, 'o';
digsep = 1; // Leading zero
}
} else {
switch p {
case 'b':
ch = advance(s);
base, prefix = 2, 'b';
case 'o':
ch = advance(s);
base, prefix = 8, 'o';
case 'd':
ch = advance(s);
base, prefix = 10, 'd';
case 'z':
ch = advance(s);
base, prefix = 12, 'z';
case 'h':
tok = Float;
fallthrough;
case 'x':
ch = advance(s);
base, prefix = 16, 'x';
case:
digsep = 1; // Leading zero
}
}
}
ch, ds = digits(s, ch, base, &invalid);
digsep |= ds;
if ch == '.' && .Scan_Floats in s.flags {
ch = advance(s);
seen_dot = true;
}
}
if seen_dot {
tok = Float;
if prefix != 0 && prefix != 'x' {
errorf(s, "invalid radix point in %s", lit_name(prefix));
}
ch, ds = digits(s, ch, base, &invalid);
digsep |= ds;
}
if digsep&1 == 0 {
errorf(s, "%s has no digits", lit_name(prefix));
}
if e := lower(ch); (e == 'e' || e == 'p') && .Scan_Floats in s.flags {
switch {
case e == 'e' && prefix != 0:
errorf(s, "%q exponent requires decimal mantissa", ch);
case e == 'p' && prefix != 'x':
errorf(s, "%q exponent requires hexadecimal mantissa", ch);
}
ch = advance(s);
tok = Float;
if ch == '+' || ch == '-' {
ch = advance(s);
}
ch, ds = digits(s, ch, 10, nil);
digsep |= ds;
if ds&1 == 0 {
error(s, "exponent has no digits");
}
} else if prefix == 'x' && tok == Float {
error(s, "hexadecimal mantissa requires a 'p' exponent");
}
if tok == Int && invalid != 0 {
errorf(s, "invalid digit %q in %s", invalid, lit_name(prefix));
}
if digsep&2 != 0 {
s.tok_end = s.src_pos - s.prev_char_len;
}
return tok, ch;
}
@(private)
scan_string :: proc(s: ^Scanner, quote: rune) -> (n: int) {
digit_val :: proc(ch: rune) -> int {
switch v := lower(ch); v {
case '0'..'9': return int(v - '0');
case 'a'..'z': return int(v - 'a');
}
return 16;
}
scan_digits :: proc(s: ^Scanner, ch: rune, base, n: int) -> rune {
ch, n := ch, n;
for n > 0 && digit_val(ch) < base {
ch = advance(s);
n -= 1;
}
if n > 0 {
error(s, "invalid char escape");
}
return ch;
}
ch := advance(s);
for ch != quote {
if ch == '\n' || ch < 0 {
error(s, "literal no terminated");
return;
}
if ch == '\\' {
ch = advance(s);
switch ch {
case quote, 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v', '\\':
ch = advance(s);
case '0'..'7': ch = scan_digits(s, advance(s), 8, 3);
case 'x': ch = scan_digits(s, advance(s), 16, 2);
case 'u': ch = scan_digits(s, advance(s), 16, 4);
case 'U': ch = scan_digits(s, advance(s), 16, 8);
case:
error(s, "invalid char escape");
}
} else {
ch = advance(s);
}
n += 1;
}
return;
}
@(private)
scan_raw_string :: proc(s: ^Scanner) {
ch := advance(s);
for ch != '`' {
if ch < 0 {
error(s, "literal not terminated");
return;
}
ch = advance(s);
}
}
@(private)
scan_char :: proc(s: ^Scanner) {
if scan_string(s, '\'') != 1 {
error(s, "invalid char literal");
}
}
@(private)
scan_comment :: proc(s: ^Scanner, ch: rune) -> rune {
ch := ch;
if ch == '/' { // line comment
ch = advance(s);
for ch != '\n' && ch >= 0 {
ch = advance(s);
}
return ch;
}
// block /**/ comment
ch = advance(s);
for {
if ch < 0 {
error(s, "comment not terminated");
break;
}
ch0 := ch;
ch = advance(s);
if ch0 == '*' && ch == '/' {
return advance(s);
}
}
return ch;
}
scan :: proc(s: ^Scanner) -> (tok: rune) {
ch := peek(s);
if ch == EOF {
return ch;
}
// reset position
s.tok_pos = -1;
s.pos.line = 0;
redo: for {
for s.whitespace & (1<<uint(ch)) != 0 {
ch = advance(s);
}
s.tok_pos = s.src_pos - s.prev_char_len;
s.pos.offset = s.tok_pos;
if s.column > 0 {
s.pos.line = s.line;
s.pos.column = s.column;
} else {
// previous character was newline
s.pos.line = s.line - 1;
s.pos.column = s.prev_line_len;
}
tok = ch;
if is_ident_rune(s, ch, 0) {
if .Scan_Idents in s.flags {
tok = Ident;
ch = scan_identifier(s);
} else {
ch = advance(s);
}
} else if is_decimal(ch) {
if s.flags >= {.Scan_Ints, .Scan_Floats} {
tok, ch = scan_number(s, ch, false);
} else {
ch = advance(s);
}
} else {
switch ch {
case EOF:
break;
case '"':
if .Scan_Strings in s.flags {
scan_string(s, '"');
tok = String;
}
ch = advance(s);
case '\'':
if .Scan_Chars in s.flags {
scan_string(s, '\'');
tok = Char;
}
ch = advance(s);
case '`':
if .Scan_Raw_Strings in s.flags {
scan_raw_string(s);
tok = Raw_String;
}
ch = advance(s);
case '.':
ch = advance(s);
if is_decimal(ch) && .Scan_Floats in s.flags {
tok, ch = scan_number(s, ch, true);
}
case '/':
ch = advance(s);
if (ch == '/' || ch == '*') && .Scan_Comments in s.flags {
if .Skip_Comments in s.flags {
s.tok_pos = -1;
ch = scan_comment(s, ch);
continue redo;
}
ch = scan_comment(s, ch);
tok = Comment;
}
case:
ch = advance(s);
}
}
break redo;
}
s.tok_end = s.src_pos - s.prev_char_len;
s.ch = ch;
return tok;
}
position :: proc(s: ^Scanner) -> Position {
pos: Position;
pos.filename = s.pos.filename;
pos.offset = s.src_pos - s.prev_char_len;
switch {
case s.column > 0:
pos.line = s.line;
pos.column = s.column;
case s.prev_line_len > 0:
pos.line = s.line-1;
pos.column = s.prev_line_len;
case:
pos.line = 1;
pos.column = 1;
}
return pos;
}
token_text :: proc(s: ^Scanner) -> string {
if s.tok_pos < 0 {
return "";
}
return string(s.src[s.tok_pos:s.tok_end]);
}
token_string :: proc(tok: rune, allocator := context.temp_allocator) -> string {
context.allocator = allocator;
switch tok {
case EOF: return strings.clone("EOF");
case Ident: return strings.clone("Ident");
case Int: return strings.clone("Int");
case Float: return strings.clone("Float");
case Char: return strings.clone("Char");
case String: return strings.clone("String");
case Raw_String: return strings.clone("Raw_String");
case Comment: return strings.clone("Comment");
}
return fmt.aprintf("%q", tok);
}
+107 -7
View File
@@ -2,17 +2,26 @@ package thread
import "core:runtime"
import "core:sync"
import "core:intrinsics"
import "core:mem"
import "intrinsics"
_ :: intrinsics;
Thread_Proc :: #type proc(^Thread);
MAX_USER_ARGUMENTS :: 8;
Thread :: struct {
using specific: Thread_Os_Specific,
procedure: Thread_Proc,
data: rawptr,
user_index: int,
using specific: Thread_Os_Specific,
procedure: Thread_Proc,
data: rawptr,
user_index: int,
user_args: [MAX_USER_ARGUMENTS]rawptr,
init_context: Maybe(runtime.Context),
creation_allocator: mem.Allocator,
}
#assert(size_of(Thread{}.user_index) == size_of(uintptr));
@@ -34,17 +43,108 @@ run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority :=
run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(rawptr))t.data;
data := rawptr(uintptr(t.user_index));
assert(t.user_index >= 1);
data := t.user_args[0];
fn(data);
destroy(t);
}
t := create(thread_proc, priority);
t.data = rawptr(fn);
t.user_index = int(uintptr(data));
t.user_index = 1;
t.user_args = data;
t.init_context = init_context;
start(t);
}
run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
where size_of(T) <= size_of(rawptr) {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T))t.data;
assert(t.user_index >= 1);
data := (^T)(&t.user_args[0])^;
fn(data);
destroy(t);
}
t := create(thread_proc, priority);
t.data = rawptr(fn);
t.user_index = 1;
data := data;
mem.copy(&t.user_args[0], &data, size_of(data));
t.init_context = init_context;
start(t);
}
run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
where size_of(T1) <= size_of(rawptr),
size_of(T2) <= size_of(rawptr) {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T1, T2))t.data;
assert(t.user_index >= 2);
arg1 := (^T1)(&t.user_args[0])^;
arg2 := (^T2)(&t.user_args[1])^;
fn(arg1, arg2);
destroy(t);
}
t := create(thread_proc, priority);
t.data = rawptr(fn);
t.user_index = 2;
arg1, arg2 := arg1, arg2;
mem.copy(&t.user_args[0], &arg1, size_of(arg1));
mem.copy(&t.user_args[1], &arg2, size_of(arg2));
t.init_context = init_context;
start(t);
}
run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
where size_of(T1) <= size_of(rawptr),
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T1, T2, T3))t.data;
assert(t.user_index >= 3);
arg1 := (^T1)(&t.user_args[0])^;
arg2 := (^T2)(&t.user_args[1])^;
arg3 := (^T3)(&t.user_args[2])^;
fn(arg1, arg2, arg3);
destroy(t);
}
t := create(thread_proc, priority);
t.data = rawptr(fn);
t.user_index = 3;
arg1, arg2, arg3 := arg1, arg2, arg3;
mem.copy(&t.user_args[0], &arg1, size_of(arg1));
mem.copy(&t.user_args[1], &arg2, size_of(arg2));
mem.copy(&t.user_args[2], &arg3, size_of(arg3));
t.init_context = init_context;
start(t);
}
run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
where size_of(T1) <= size_of(rawptr),
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T1, T2, T3, T4))t.data;
assert(t.user_index >= 4);
arg1 := (^T1)(&t.user_args[0])^;
arg2 := (^T2)(&t.user_args[1])^;
arg3 := (^T3)(&t.user_args[2])^;
arg4 := (^T4)(&t.user_args[3])^;
fn(arg1, arg2, arg3, arg4);
destroy(t);
}
t := create(thread_proc, priority);
t.data = rawptr(fn);
t.user_index = 4;
arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4;
mem.copy(&t.user_args[0], &arg1, size_of(arg1));
mem.copy(&t.user_args[1], &arg2, size_of(arg2));
mem.copy(&t.user_args[2], &arg3, size_of(arg3));
mem.copy(&t.user_args[3], &arg4, size_of(arg4));
t.init_context = init_context;
start(t);
}
create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread {
t := create(fn, priority);
+3 -2
View File
@@ -85,6 +85,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
if thread == nil {
return nil;
}
thread.creation_allocator = context.allocator;
// Set thread priority.
policy: i32;
@@ -106,7 +107,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
sync.mutex_init(&thread.start_mutex);
sync.condition_init(&thread.start_gate, &thread.start_mutex);
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
free(thread);
free(thread, thread.creation_allocator);
return nil;
}
thread.procedure = procedure;
@@ -172,7 +173,7 @@ join_multiple :: proc(threads: ..^Thread) {
destroy :: proc(t: ^Thread) {
join(t);
t.unix_thread = {};
free(t);
free(t, t.creation_allocator);
}
+6 -2
View File
@@ -49,10 +49,14 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
thread := new(Thread);
if thread == nil {
return nil;
}
thread.creation_allocator = context.allocator;
win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
if win32_thread == nil {
free(thread);
free(thread, thread.creation_allocator);
return nil;
}
thread.procedure = procedure;
@@ -111,7 +115,7 @@ join_multiple :: proc(threads: ..^Thread) {
destroy :: proc(thread: ^Thread) {
join(thread);
free(thread);
free(thread, thread.creation_allocator);
}
terminate :: proc(using thread : ^Thread, exit_code: u32) {
+42 -996
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+41
View File
@@ -350,3 +350,44 @@ rune_size :: proc(r: rune) -> int {
}
return -1;
}
// full_rune reports if the bytes in b begin with a full utf-8 encoding of a rune or not
// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
full_rune :: proc(b: []byte) -> bool {
n := len(b);
if n == 0 {
return false;
}
x := _first[b[0]];
if n >= int(x & 7) {
return true;
}
accept := accept_ranges[x>>4];
if n > 1 && (b[1] < accept.lo || accept.hi < b[1]) {
return true;
} else if n > 2 && (b[2] < LOCB || HICB < b[2]) {
return true;
}
return false;
}
// full_rune_in_string reports if the bytes in s begin with a full utf-8 encoding of a rune or not
// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
full_rune_in_string :: proc(s: string) -> bool {
return full_rune(transmute([]byte)s);
}
_first := [256]u8{
0x00..0x7f = 0xf0, // ascii, size 1
0x80..0xc1 = 0xf1, // invalid, size 1
0xc2..0xdf = 0x02, // accept 1, size 2
0xe0 = 0x13, // accept 1, size 3
0xe1..0xec = 0x03, // accept 0, size 3
0xed = 0x23, // accept 2, size 3
0xee..0xef = 0x03, // accept 0, size 3
0xf0 = 0x34, // accept 3, size 4
0xf1..0xf3 = 0x04, // accept 0, size 4
0xf4 = 0x44, // accept 4, size 4
0xf5..0xff = 0xf1, // ascii, size 1
};