mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-16 08:01:24 -07:00
Merge branch 'master' into parser-experiments
This commit is contained in:
@@ -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);
|
||||
},
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
},
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,3 +1,3 @@
|
||||
package dynlib
|
||||
|
||||
Library :: opaque rawptr;
|
||||
Library :: #opaque rawptr;
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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) ---
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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));
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
};
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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) ---;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user