Merge pull request #1112 from odin-lang/optional-semicolons

Optional Semicolons
This commit is contained in:
gingerBill
2021-09-06 19:29:08 +01:00
committed by GitHub
242 changed files with 38583 additions and 40484 deletions
+23 -23
View File
@@ -14,14 +14,14 @@ Loadahead_Reader :: struct {
}
lookahead_reader_init :: proc(lr: ^Loadahead_Reader, r: io.Reader, buf: []byte) -> ^Loadahead_Reader {
lr.r = r;
lr.buf = buf;
lr.n = 0;
return lr;
lr.r = r
lr.buf = buf
lr.n = 0
return lr
}
lookahead_reader_buffer :: proc(lr: ^Loadahead_Reader) -> []byte {
return lr.buf[:lr.n];
return lr.buf[:lr.n]
}
@@ -31,35 +31,35 @@ lookahead_reader_buffer :: proc(lr: ^Loadahead_Reader) -> []byte {
lookahead_reader_peek :: proc(lr: ^Loadahead_Reader, n: int) -> ([]byte, io.Error) {
switch {
case n < 0:
return nil, .Negative_Read;
return nil, .Negative_Read
case n > len(lr.buf):
return nil, .Buffer_Full;
return nil, .Buffer_Full
}
n := n;
err: io.Error;
read_count: int;
n := n
err: io.Error
read_count: int
if lr.n < n {
read_count, err = io.read_at_least(lr.r, lr.buf[lr.n:], n-lr.n);
read_count, err = io.read_at_least(lr.r, lr.buf[lr.n:], n-lr.n)
if err == .Unexpected_EOF {
err = .EOF;
err = .EOF
}
}
lr.n += read_count;
lr.n += read_count
if n > lr.n {
n = lr.n;
n = lr.n
}
return lr.buf[:n], err;
return lr.buf[:n], err
}
// lookahead_reader_peek_all returns a slice of the Lookahead_Reader populating the full buffer
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
// NOTE: The returned buffer is not a copy of the underlying buffer
lookahead_reader_peek_all :: proc(lr: ^Loadahead_Reader) -> ([]byte, io.Error) {
return lookahead_reader_peek(lr, len(lr.buf));
return lookahead_reader_peek(lr, len(lr.buf))
}
@@ -67,17 +67,17 @@ lookahead_reader_peek_all :: proc(lr: ^Loadahead_Reader) -> ([]byte, io.Error) {
lookahead_reader_consume :: proc(lr: ^Loadahead_Reader, n: int) -> io.Error {
switch {
case n == 0:
return nil;
return nil
case n < 0:
return .Negative_Read;
return .Negative_Read
case lr.n < n:
return .Short_Buffer;
return .Short_Buffer
}
copy(lr.buf, lr.buf[n:lr.n]);
lr.n -= n;
return nil;
copy(lr.buf, lr.buf[n:lr.n])
lr.n -= n
return nil
}
lookahead_reader_consume_all :: proc(lr: ^Loadahead_Reader) -> io.Error {
return lookahead_reader_consume(lr, lr.n);
return lookahead_reader_consume(lr, lr.n)
}
+27 -27
View File
@@ -10,59 +10,59 @@ Read_Writer :: struct {
read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
rw.r, rw.w = r, w;
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;
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
b := (^Read_Writer)(s.stream_data).w
return writer_read_from(b, r)
},
};
}
+177 -177
View File
@@ -22,85 +22,85 @@ Reader :: struct {
}
DEFAULT_BUF_SIZE :: 4096;
DEFAULT_BUF_SIZE :: 4096
@(private)
MIN_READ_BUFFER_SIZE :: 16;
MIN_READ_BUFFER_SIZE :: 16
@(private)
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128;
DEFAULT_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);
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_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^ = {};
delete(b.buf, b.buf_allocator)
b^ = {}
}
reader_size :: proc(b: ^Reader) -> int {
return len(b.buf);
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;
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;
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;
return .Buffer_Full
}
if b.max_consecutive_empty_reads <= 0 {
b.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
b.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS
}
// read new data, and try a limited number of times
for i := b.max_consecutive_empty_reads; i > 0; i -= 1 {
n, err := io.read(b.rd, b.buf[b.w:]);
n, err := io.read(b.rd, b.buf[b.w:])
if n < 0 {
return .Negative_Read;
return .Negative_Read
}
b.w += n;
b.w += n
if err != nil {
b.err = err;
return nil;
b.err = err
return nil
}
if n > 0 {
return nil;
return nil
}
}
b.err = .No_Progress;
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;
err := b.err
b.err = nil
return err
}
// reader_peek returns the next n bytes without advancing the reader
@@ -109,151 +109,151 @@ _reader_consume_err :: proc(b: ^Reader) -> io.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;
n := n
if n < 0 {
return nil, .Negative_Count;
return nil, .Negative_Count
}
b.last_byte = -1;
b.last_rune_size = -1;
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;
return nil, fill_err
}
}
if n > len(b.buf) {
return b.buf[b.r : b.w], .Buffer_Full;
return b.buf[b.r : b.w], .Buffer_Full
}
if available := b.w - b.r; available < n {
n = available;
err = _reader_consume_err(b);
n = available
err = _reader_consume_err(b)
if err == nil {
err = .Buffer_Full;
err = .Buffer_Full
}
}
return b.buf[b.r : b.r+n], err;
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;
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;
return 0, .Negative_Count
}
if n == 0 {
return;
return
}
remaining := n;
remaining := n
for {
skip := reader_buffered(b);
skip := reader_buffered(b)
if skip == 0 {
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
return 0, fill_err;
return 0, fill_err
}
skip = reader_buffered(b);
skip = reader_buffered(b)
}
skip = min(skip, remaining);
b.r += skip;
remaining -= skip;
skip = min(skip, remaining)
b.r += skip
remaining -= skip
if remaining == 0 {
return n, nil;
return n, nil
}
if b.err != nil {
return n - remaining, _reader_consume_err(b);
return n - remaining, _reader_consume_err(b)
}
}
return;
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);
n = len(p)
if n == 0 {
if reader_buffered(b) > 0 {
return 0, nil;
return 0, nil
}
return 0, _reader_consume_err(b);
return 0, _reader_consume_err(b)
}
if b.r == b.w {
if b.err != nil {
return 0, _reader_consume_err(b);
return 0, _reader_consume_err(b)
}
if len(p) >= len(b.buf) {
n, b.err = io.read(b.rd, p);
n, b.err = io.read(b.rd, p)
if n < 0 {
return 0, .Negative_Read;
return 0, .Negative_Read
}
if n > 0 {
b.last_byte = int(p[n-1]);
b.last_rune_size = -1;
b.last_byte = int(p[n-1])
b.last_rune_size = -1
}
return n, _reader_consume_err(b);
return n, _reader_consume_err(b)
}
b.r, b.w = 0, 0;
n, b.err = io.read(b.rd, b.buf);
b.r, b.w = 0, 0
n, b.err = io.read(b.rd, b.buf)
if n < 0 {
return 0, .Negative_Read;
return 0, .Negative_Read
}
if n == 0 {
return 0, _reader_consume_err(b);
return 0, _reader_consume_err(b)
}
b.w += n;
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;
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;
b.last_rune_size = -1
for b.r == b.w {
if b.err != nil {
return 0, _reader_consume_err(b);
return 0, _reader_consume_err(b)
}
if err := _reader_read_new_chunk(b); err != nil {
return 0, err;
return 0, err
}
}
c := b.buf[b.r];
b.r += 1;
b.last_byte = int(c);
return c, nil;
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;
return .Invalid_Unread
}
if b.r > 0 {
b.r -= 1;
b.r -= 1
} else {
// b.r == 0 && b.w == 0
b.w = 1;
b.w = 1
}
b.buf[b.r] = byte(b.last_byte);
b.last_byte = -1;
b.last_rune_size = -1;
return nil;
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
@@ -265,96 +265,96 @@ reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) {
b.err == nil &&
b.w-b.w < len(b.buf) {
if err = _reader_read_new_chunk(b); err != nil {
return;
return
}
}
b.last_rune_size = -1;
b.last_rune_size = -1
if b.r == b.w {
err = _reader_consume_err(b);
return;
err = _reader_consume_err(b)
return
}
r, size = rune(b.buf[b.r]), 1;
r, size = rune(b.buf[b.r]), 1
if r >= utf8.RUNE_SELF {
r, size = utf8.decode_rune(b.buf[b.r : b.w]);
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;
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;
return .Invalid_Unread
}
b.r -= b.last_rune_size;
b.last_byte = -1;
b.last_rune_size = -1;
return nil;
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]);
n, err := io.write(w, b.buf[b.r:b.w])
if n < 0 {
return 0, .Negative_Write;
return 0, .Negative_Write
}
b.r += n;
return i64(n), err;
b.r += n
return i64(n), err
}
n, err = write_buf(b, w);
n, err = write_buf(b, w)
if err != nil {
return;
return
}
m: i64;
m: i64
if nr, ok := io.to_writer_to(b.rd); ok {
m, err = io.write_to(nr, w);
n += m;
return n, err;
m, err = io.write_to(nr, w)
n += m
return n, err
}
if nw, ok := io.to_reader_from(w); ok {
m, err = io.read_from(nw, b.rd);
n += m;
return n, err;
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;
return
}
}
for b.r < b.w {
m, err = write_buf(b, w);
n += m;
m, err = write_buf(b, w)
n += m
if err != nil {
return;
return
}
if err = _reader_read_new_chunk(b); err != nil {
return;
return
}
}
if b.err == .EOF {
b.err = nil;
b.err = nil
}
err = _reader_consume_err(b);
return;
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;
s.stream_data = b
s.stream_vtable = _reader_vtable
return
}
@@ -362,35 +362,35 @@ reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
@(private)
_reader_vtable := &io.Stream_VTable{
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Reader)(s.stream_data);
reader_destroy(b);
return nil;
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);
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);
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);
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);
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);
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);
b := (^Reader)(s.stream_data)
return reader_write_to(b, w)
},
};
}
@@ -410,71 +410,71 @@ _reader_vtable := &io.Stream_VTable{
// 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;
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;
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;
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;
b.r = b.w
line = b.buf
err = .Buffer_Full
break
}
s = b.w - b.r;
s = b.w - b.r
if err = _reader_read_new_chunk(b); err != nil {
break;
break
}
}
if i := len(line)-1; i >= 0 {
b.last_byte = int(line[i]);
b.last_rune_size = -1;
b.last_byte = int(line[i])
b.last_rune_size = -1
}
return;
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;
full: [dynamic]byte
full.allocator = allocator
frag: []byte;
frag: []byte
for {
e: io.Error;
frag, e = reader_read_slice(b, delim);
e: io.Error
frag, e = reader_read_slice(b, delim)
if e == nil {
break;
break
}
if e != .Buffer_Full {
err = e;
break;
err = e
break
}
append(&full, ..frag);
append(&full, ..frag)
}
append(&full, ..frag);
return full[:], err;
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;
buf, err := reader_read_bytes(b, delim, allocator)
return string(buf), err
}
+119 -119
View File
@@ -21,7 +21,7 @@ Scanner_Error :: union {
}
// Split_Proc is the signature of the split procedure used to tokenize the input.
Split_Proc :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool);
Split_Proc :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool)
Scanner :: struct {
r: io.Reader,
@@ -40,28 +40,28 @@ Scanner :: struct {
done: bool,
}
DEFAULT_MAX_SCAN_TOKEN_SIZE :: 1<<16;
DEFAULT_MAX_SCAN_TOKEN_SIZE :: 1<<16
@(private)
_INIT_BUF_SIZE :: 4096;
_INIT_BUF_SIZE :: 4096
scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocator) -> ^Scanner {
s.r = r;
s.split = scan_lines;
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE;
s.buf.allocator = buf_allocator;
return s;
s.r = r
s.split = scan_lines
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE
s.buf.allocator = buf_allocator
return s
}
scanner_init_with_buffer :: proc(s: ^Scanner, r: io.Reader, buf: []byte) -> ^Scanner {
s.r = r;
s.split = scan_lines;
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE;
s.buf = mem.buffer_from_slice(buf);
resize(&s.buf, cap(s.buf));
return s;
s.r = r
s.split = scan_lines
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE
s.buf = mem.buffer_from_slice(buf)
resize(&s.buf, cap(s.buf))
return s
}
scanner_destroy :: proc(s: ^Scanner) {
delete(s.buf);
delete(s.buf)
}
@@ -69,9 +69,9 @@ scanner_destroy :: proc(s: ^Scanner) {
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
switch s._err {
case .EOF, .None:
return nil;
return nil
}
return s._err;
return s._err
}
// Returns the most recent token created by scanner_scan.
@@ -79,7 +79,7 @@ scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
// by another call to scanner_scan.
// Treat the returned value as if it is immutable.
scanner_bytes :: proc(s: ^Scanner) -> []byte {
return s.token;
return s.token
}
// Returns the most recent token created by scanner_scan.
@@ -87,146 +87,146 @@ scanner_bytes :: proc(s: ^Scanner) -> []byte {
// by another call to scanner_scan.
// Treat the returned value as if it is immutable.
scanner_text :: proc(s: ^Scanner) -> string {
return string(s.token);
return string(s.token)
}
// scanner_scan advances the scanner
scanner_scan :: proc(s: ^Scanner) -> bool {
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
err := err;
err := err
if err == .None {
err = nil;
err = nil
}
switch s._err {
case nil, .EOF:
s._err = err;
s._err = err
}
}
if s.done {
return false;
return false
}
s.scan_called = true;
s.scan_called = true
for {
// Check if a token is possible with what is available
// Allow the split procedure to recover if it fails
if s.start < s.end || s._err != nil {
advance, token, err, final_token := s.split(s.buf[s.start:s.end], s._err != nil);
advance, token, err, final_token := s.split(s.buf[s.start:s.end], s._err != nil)
if final_token {
s.token = token;
s.done = true;
return true;
s.token = token
s.done = true
return true
}
if err != nil {
set_err(s, err);
return false;
set_err(s, err)
return false
}
// Do advance
if advance < 0 {
set_err(s, .Negative_Advance);
return false;
set_err(s, .Negative_Advance)
return false
}
if advance > s.end-s.start {
set_err(s, .Advanced_Too_Far);
return false;
set_err(s, .Advanced_Too_Far)
return false
}
s.start += advance;
s.start += advance
s.token = token;
s.token = token
if s.token != nil {
if s._err == nil || advance > 0 {
s.successive_empty_token_count = 0;
s.successive_empty_token_count = 0
} else {
s.successive_empty_token_count += 1;
s.successive_empty_token_count += 1
if s.max_consecutive_empty_reads <= 0 {
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS
}
if s.successive_empty_token_count > s.max_consecutive_empty_reads {
set_err(s, .No_Progress);
return false;
set_err(s, .No_Progress)
return false
}
}
return true;
return true
}
}
// If an error is hit, no token can be created
if s._err != nil {
s.start = 0;
s.end = 0;
return false;
s.start = 0
s.end = 0
return false
}
// More data must be required to be read
if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
copy(s.buf[:], s.buf[s.start:s.end]);
s.end -= s.start;
s.start = 0;
copy(s.buf[:], s.buf[s.start:s.end])
s.end -= s.start
s.start = 0
}
could_be_too_short := false;
could_be_too_short := false
// Resize the buffer if full
if s.end == len(s.buf) {
if s.max_token_size <= 0 {
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE;
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE
}
if len(s.buf) >= s.max_token_size {
set_err(s, .Too_Long);
return false;
set_err(s, .Too_Long)
return false
}
// overflow check
new_size := _INIT_BUF_SIZE;
new_size := _INIT_BUF_SIZE
if len(s.buf) > 0 {
overflowed: bool;
overflowed: bool
if new_size, overflowed = intrinsics.overflow_mul(len(s.buf), 2); overflowed {
set_err(s, .Too_Long);
return false;
set_err(s, .Too_Long)
return false
}
}
old_size := len(s.buf);
new_size = min(new_size, s.max_token_size);
resize(&s.buf, new_size);
s.end -= s.start;
s.start = 0;
old_size := len(s.buf)
new_size = min(new_size, s.max_token_size)
resize(&s.buf, new_size)
s.end -= s.start
s.start = 0
could_be_too_short = old_size >= len(s.buf);
could_be_too_short = old_size >= len(s.buf)
}
// Read data into the buffer
loop := 0;
loop := 0
for {
n, err := io.read(s.r, s.buf[s.end:len(s.buf)]);
n, err := io.read(s.r, s.buf[s.end:len(s.buf)])
if n < 0 || len(s.buf)-s.end < n {
set_err(s, .Bad_Read_Count);
break;
set_err(s, .Bad_Read_Count)
break
}
s.end += n;
s.end += n
if err != nil {
set_err(s, err);
break;
set_err(s, err)
break
}
if n > 0 {
s.successive_empty_token_count = 0;
break;
s.successive_empty_token_count = 0
break
}
loop += 1;
loop += 1
if s.max_consecutive_empty_reads <= 0 {
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS
}
if loop > s.max_consecutive_empty_reads {
if could_be_too_short {
set_err(s, .Too_Short);
set_err(s, .Too_Short)
} else {
set_err(s, .No_Progress);
set_err(s, .No_Progress)
}
break;
break
}
}
}
@@ -234,38 +234,38 @@ scanner_scan :: proc(s: ^Scanner) -> bool {
scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
if at_eof && len(data) == 0 {
return;
return
}
return 1, data[0:1], nil, false;
return 1, data[0:1], nil, false
}
scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
if at_eof && len(data) == 0 {
return;
return
}
if data[0] < utf8.RUNE_SELF {
advance = 1;
token = data[0:1];
return;
advance = 1
token = data[0:1]
return
}
_, width := utf8.decode_rune(data);
_, width := utf8.decode_rune(data)
if width > 1 {
advance = width;
token = data[0:width];
return;
advance = width
token = data[0:width]
return
}
if !at_eof && !utf8.full_rune(data) {
return;
return
}
@thread_local ERROR_RUNE := []byte{0xef, 0xbf, 0xbd};
@thread_local ERROR_RUNE := []byte{0xef, 0xbf, 0xbd}
advance = 1;
token = ERROR_RUNE;
return;
advance = 1
token = ERROR_RUNE
return
}
scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
@@ -273,68 +273,68 @@ scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
switch r {
// lower ones
case ' ', '\t', '\n', '\v', '\f', '\r':
return true;
return true
case '\u0085', '\u00a0':
return true;
return true
// higher ones
case '\u2000' ..= '\u200a':
return true;
return true
case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
return true;
return true
}
return false;
return false
}
// skip spaces at the beginning
start := 0;
start := 0
for width := 0; start < len(data); start += width {
r: rune;
r, width = utf8.decode_rune(data[start:]);
r: rune
r, width = utf8.decode_rune(data[start:])
if !is_space(r) {
break;
break
}
}
for width, i := 0, start; i < len(data); i += width {
r: rune;
r, width = utf8.decode_rune(data[i:]);
r: rune
r, width = utf8.decode_rune(data[i:])
if is_space(r) {
advance = i+width;
token = data[start:i];
return;
advance = i+width
token = data[start:i]
return
}
}
if at_eof && len(data) > start {
advance = len(data);
token = data[start:];
return;
advance = len(data)
token = data[start:]
return
}
advance = start;
return;
advance = start
return
}
scan_lines :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
trim_carriage_return :: proc "contextless" (data: []byte) -> []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {
return data[0:len(data)-1];
return data[0:len(data)-1]
}
return data;
return data
}
if at_eof && len(data) == 0 {
return;
return
}
if i := bytes.index_byte(data, '\n'); i >= 0 {
advance = i+1;
token = trim_carriage_return(data[0:i]);
return;
advance = i+1
token = trim_carriage_return(data[0:i])
return
}
if at_eof {
advance = len(data);
token = trim_carriage_return(data);
advance = len(data)
token = trim_carriage_return(data)
}
return;
return
}
+96 -96
View File
@@ -20,150 +20,150 @@ Writer :: struct {
}
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);
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_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^ = {};
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);
return len(b.buf)
}
writer_reset :: proc(b: ^Writer, w: io.Writer) {
b.wr = w;
b.n = 0;
b.err = nil;
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;
return b.err
}
if b.n == 0 {
return nil;
return nil
}
n, err := io.write(b.wr, b.buf[0:b.n]);
n, err := io.write(b.wr, b.buf[0:b.n])
if n < b.n && err == nil {
err = .Short_Write;
err = .Short_Write
}
if err != nil {
if n > 0 && n < b.n {
copy(b.buf[:b.n-n], b.buf[n : b.n]);
copy(b.buf[:b.n-n], b.buf[n : b.n])
}
b.n -= n;
b.err = err;
return err;
b.n -= n
b.err = err
return err
}
b.n = 0;
return nil;
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;
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;
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;
p := p
for len(p) > writer_available(b) && b.err == nil {
m: int;
m: int
if writer_buffered(b) == 0 {
m, b.err = io.write(b.wr, p);
m, b.err = io.write(b.wr, p)
} else {
m = copy(b.buf[b.n:], p);
b.n += m;
writer_flush(b);
m = copy(b.buf[b.n:], p)
b.n += m
writer_flush(b)
}
n += m;
p = p[m:];
n += m
p = p[m:]
}
if b.err != nil {
return n, b.err;
return n, b.err
}
m := copy(b.buf[b.n:], p);
b.n += m;
m += n;
return m, nil;
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;
return b.err
}
if writer_available(b) <= 0 && writer_flush(b) != nil {
return b.err;
return b.err
}
b.buf[b.n] = c;
b.n += 1;
return nil;
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;
err = writer_write_byte(b, byte(r))
size = 0 if err != nil else 1
return
}
if b.err != nil {
return 0, b.err;
return 0, b.err
}
buf: [4]u8;
buf: [4]u8
n := writer_available(b);
n := writer_available(b)
if n < utf8.UTF_MAX {
writer_flush(b);
writer_flush(b)
if b.err != nil {
return 0, b.err;
return 0, b.err
}
n = writer_available(b);
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]);
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;
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);
return writer_write(b, transmute([]byte)s)
}
// writer_read_from is to support io.Reader_From types
@@ -171,60 +171,60 @@ writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) {
// 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;
return 0, b.err
}
if writer_buffered(b) == 0 {
if w, ok := io.to_reader_from(b.wr); !ok {
n, err = io.read_from(w, r);
b.err = err;
return;
n, err = io.read_from(w, r)
b.err = err
return
}
}
for {
if writer_available(b) == 0 {
writer_flush(b) or_return;
writer_flush(b) or_return
}
if b.max_consecutive_empty_writes <= 0 {
b.max_consecutive_empty_writes = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
b.max_consecutive_empty_writes = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS
}
m: int;
nr := 0;
m: int
nr := 0
for nr < b.max_consecutive_empty_writes {
m, err = io.read(r, b.buf[b.n:]);
m, err = io.read(r, b.buf[b.n:])
if m != 0 || err != nil {
break;
break
}
nr += 1;
nr += 1
}
if nr == b.max_consecutive_empty_writes {
return n, .No_Progress;
return n, .No_Progress
}
b.n += m;
n += i64(m);
b.n += m
n += i64(m)
if err != nil {
break;
break
}
}
if err == .EOF {
if writer_available(b) == 0 {
err = writer_flush(b);
err = writer_flush(b)
} else {
err = nil;
err = nil
}
}
return;
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;
s.stream_data = b
s.stream_vtable = _writer_vtable
return
}
@@ -232,28 +232,28 @@ writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
@(private)
_writer_vtable := &io.Stream_VTable{
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Writer)(s.stream_data);
writer_destroy(b);
return nil;
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);
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);
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);
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);
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);
b := (^Writer)(s.stream_data)
return writer_read_from(b, r)
},
};
}
+187 -187
View File
@@ -3,10 +3,10 @@ package bytes
import "core:io"
import "core:unicode/utf8"
MIN_READ :: 512;
MIN_READ :: 512
@(private)
SMALL_BUFFER_SIZE :: 64;
SMALL_BUFFER_SIZE :: 64
// A Buffer is a variable-sized buffer of bytes with a io.Stream interface
// The zero value for Buffer is an empty buffer ready to use.
@@ -28,406 +28,406 @@ Read_Op :: enum i8 {
buffer_init :: proc(b: ^Buffer, buf: []byte) {
resize(&b.buf, len(buf));
copy(b.buf[:], buf);
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);
resize(&b.buf, len(s))
copy(b.buf[:], s)
}
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) {
b.buf.allocator = allocator;
reserve(&b.buf, cap);
resize(&b.buf, len);
b.buf.allocator = allocator
reserve(&b.buf, cap)
resize(&b.buf, len)
}
buffer_destroy :: proc(b: ^Buffer) {
delete(b.buf);
buffer_reset(b);
delete(b.buf)
buffer_reset(b)
}
buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
return b.buf[b.off:];
return b.buf[b.off:]
}
buffer_to_string :: proc(b: ^Buffer) -> string {
if b == nil {
return "<nil>";
return "<nil>"
}
return string(b.buf[b.off:]);
return string(b.buf[b.off:])
}
buffer_is_empty :: proc(b: ^Buffer) -> bool {
return len(b.buf) <= b.off;
return len(b.buf) <= b.off
}
buffer_length :: proc(b: ^Buffer) -> int {
return len(b.buf) - b.off;
return len(b.buf) - b.off
}
buffer_capacity :: proc(b: ^Buffer) -> int {
return cap(b.buf);
return cap(b.buf)
}
buffer_reset :: proc(b: ^Buffer) {
clear(&b.buf);
b.off = 0;
b.last_read = .Invalid;
clear(&b.buf)
b.off = 0
b.last_read = .Invalid
}
buffer_truncate :: proc(b: ^Buffer, n: int) {
if n == 0 {
buffer_reset(b);
return;
buffer_reset(b)
return
}
b.last_read = .Invalid;
b.last_read = .Invalid
if n < 0 || n > buffer_length(b) {
panic("bytes.truncate: truncation out of range");
panic("bytes.truncate: truncation out of range")
}
resize(&b.buf, b.off+n);
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;
resize(&b.buf, l+n)
return l, true
}
return 0, false;
return 0, false
}
@(private)
_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
m := buffer_length(b);
m := buffer_length(b)
if m == 0 && b.off != 0 {
buffer_reset(b);
buffer_reset(b)
}
if i, ok := _buffer_try_grow(b, n); ok {
return i;
return i
}
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
return 0;
b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE)
return 0
}
c := cap(b.buf);
c := cap(b.buf)
if n <= c/2 - m {
copy(b.buf[:], b.buf[b.off:]);
copy(b.buf[:], b.buf[b.off:])
} else if c > max(int) - c - n {
panic("bytes.Buffer: too large");
panic("bytes.Buffer: too large")
} else {
resize(&b.buf, 2*c + n);
copy(b.buf[:], b.buf[b.off:]);
resize(&b.buf, 2*c + n)
copy(b.buf[:], b.buf[b.off:])
}
b.off = 0;
resize(&b.buf, m+n);
return m;
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");
panic("bytes.buffer_grow: negative count")
}
m := _buffer_grow(b, n);
resize(&b.buf, m);
m := _buffer_grow(b, n)
resize(&b.buf, m)
}
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
b.last_read = .Invalid
if offset < 0 {
err = .Invalid_Offset;
return;
err = .Invalid_Offset
return
}
_, ok := _buffer_try_grow(b, offset+len(p));
_, ok := _buffer_try_grow(b, offset+len(p))
if !ok {
_ = _buffer_grow(b, offset+len(p));
_ = _buffer_grow(b, offset+len(p))
}
if len(b.buf) <= offset {
return 0, .Short_Write;
return 0, .Short_Write
}
return copy(b.buf[offset:], p), nil;
return copy(b.buf[offset:], p), nil
}
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, len(p));
b.last_read = .Invalid
m, ok := _buffer_try_grow(b, len(p))
if !ok {
m = _buffer_grow(b, len(p));
m = _buffer_grow(b, len(p))
}
return copy(b.buf[m:], p), nil;
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));
b.last_read = .Invalid
m, ok := _buffer_try_grow(b, len(s))
if !ok {
m = _buffer_grow(b, len(s));
m = _buffer_grow(b, len(s))
}
return copy(b.buf[m:], s), nil;
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);
b.last_read = .Invalid
m, ok := _buffer_try_grow(b, 1)
if !ok {
m = _buffer_grow(b, 1);
m = _buffer_grow(b, 1)
}
b.buf[m] = c;
return nil;
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;
buffer_write_byte(b, byte(r))
return 1, nil
}
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
b.last_read = .Invalid
m, ok := _buffer_try_grow(b, utf8.UTF_MAX)
if !ok {
m = _buffer_grow(b, utf8.UTF_MAX);
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;
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);
n := n
b.last_read = .Invalid
m := buffer_length(b)
if n > m {
n = m;
n = m
}
data := b.buf[b.off : b.off + n];
b.off += n;
data := b.buf[b.off : b.off + n]
b.off += n
if n > 0 {
b.last_read = .Read;
b.last_read = .Read
}
return data;
return data
}
buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
b.last_read = .Invalid
if buffer_is_empty(b) {
buffer_reset(b);
buffer_reset(b)
if len(p) == 0 {
return 0, nil;
return 0, nil
}
return 0, .EOF;
return 0, .EOF
}
n = copy(p, b.buf[b.off:]);
b.off += n;
n = copy(p, b.buf[b.off:])
b.off += n
if n > 0 {
b.last_read = .Read;
b.last_read = .Read
}
return;
return
}
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
b.last_read = .Invalid
if offset < 0 || offset >= len(b.buf) {
err = .Invalid_Offset;
return;
err = .Invalid_Offset
return
}
if 0 <= offset && offset < len(b.buf) {
n = copy(p, b.buf[offset:]);
n = copy(p, b.buf[offset:])
}
if n > 0 {
b.last_read = .Read;
b.last_read = .Read
}
return;
return
}
buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
if buffer_is_empty(b) {
buffer_reset(b);
return 0, .EOF;
buffer_reset(b)
return 0, .EOF
}
c := b.buf[b.off];
b.off += 1;
b.last_read = .Read;
return c, nil;
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;
buffer_reset(b)
return 0, 0, .EOF
}
c := b.buf[b.off];
c := b.buf[b.off]
if c < utf8.RUNE_SELF {
b.off += 1;
b.last_read = .Read_Rune1;
return rune(c), 1, nil;
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;
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;
return .Invalid_Unread
}
b.last_read = .Invalid;
b.last_read = .Invalid
if b.off > 0 {
b.off -= 1;
b.off -= 1
}
return nil;
return nil
}
buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
if b.last_read <= .Invalid {
return .Invalid_Unread;
return .Invalid_Unread
}
if b.off >= int(b.last_read) {
b.off -= int(i8(b.last_read));
b.off -= int(i8(b.last_read))
}
b.last_read = .Invalid;
return nil;
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;
i := index_byte(b.buf[b.off:], delim)
end := b.off + i + 1
if i < 0 {
end = len(b.buf);
err = .EOF;
end = len(b.buf)
err = .EOF
}
line = b.buf[b.off:end];
b.off = end;
b.last_read = .Read;
return;
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;
slice: []byte
slice, err = buffer_read_bytes(b, delim)
return string(slice), err
}
buffer_write_to :: proc(b: ^Buffer, w: io.Writer) -> (n: i64, err: io.Error) {
b.last_read = .Invalid;
b.last_read = .Invalid
if byte_count := buffer_length(b); byte_count > 0 {
m, e := io.write(w, b.buf[b.off:]);
m, e := io.write(w, b.buf[b.off:])
if m > byte_count {
panic("bytes.buffer_write_to: invalid io.write count");
panic("bytes.buffer_write_to: invalid io.write count")
}
b.off += m;
n = i64(m);
b.off += m
n = i64(m)
if e != nil {
err = e;
return;
err = e
return
}
if m != byte_count {
err = .Short_Write;
return;
err = .Short_Write
return
}
}
buffer_reset(b);
return;
buffer_reset(b)
return
}
buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #no_bounds_check {
b.last_read = .Invalid;
b.last_read = .Invalid
for {
i := _buffer_grow(b, MIN_READ);
resize(&b.buf, i);
m, e := io.read(r, b.buf[i:cap(b.buf)]);
i := _buffer_grow(b, MIN_READ)
resize(&b.buf, i)
m, e := io.read(r, b.buf[i:cap(b.buf)])
if m < 0 {
err = .Negative_Read;
return;
err = .Negative_Read
return
}
resize(&b.buf, i+m);
n += i64(m);
resize(&b.buf, i+m)
n += i64(m)
if e == .EOF {
return;
return
}
if e != nil {
err = e;
return;
err = e
return
}
}
return;
return
}
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
s.stream_data = b;
s.stream_vtable = _buffer_vtable;
return;
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));
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);
b := (^Buffer)(s.stream_data)
return buffer_read(b, p)
},
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read_at(b, p, int(offset));
b := (^Buffer)(s.stream_data)
return buffer_read_at(b, p, int(offset))
},
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read_byte(b);
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);
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);
b := (^Buffer)(s.stream_data)
return buffer_write(b, p)
},
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_write_at(b, p, int(offset));
b := (^Buffer)(s.stream_data)
return buffer_write_at(b, p, int(offset))
},
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
b := (^Buffer)(s.stream_data);
return buffer_write_byte(b, c);
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);
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);
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);
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;
b := (^Buffer)(s.stream_data)
buffer_destroy(b)
return nil
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_write_to(b, w);
b := (^Buffer)(s.stream_data)
return buffer_write_to(b, w)
},
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read_from(b, r);
b := (^Buffer)(s.stream_data)
return buffer_read_from(b, r)
},
};
}
+419 -419
View File
File diff suppressed because it is too large Load Diff
+79 -79
View File
@@ -10,168 +10,168 @@ Reader :: struct {
}
reader_init :: proc(r: ^Reader, s: []byte) {
r.s = s;
r.i = 0;
r.prev_rune = -1;
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;
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 0
}
return int(i64(len(r.s)) - r.i);
return int(i64(len(r.s)) - r.i)
}
reader_size :: proc(r: ^Reader) -> i64 {
return i64(len(r.s));
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;
return 0, .EOF
}
r.prev_rune = -1;
n = copy(p, r.s[r.i:]);
r.i += i64(n);
return;
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;
return 0, .Invalid_Offset
}
if off >= i64(len(r.s)) {
return 0, .EOF;
return 0, .EOF
}
n = copy(p, r.s[off:]);
n = copy(p, r.s[off:])
if n < len(p) {
err = .EOF;
err = .EOF
}
return;
return
}
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.prev_rune = -1;
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
return 0, .EOF;
return 0, .EOF
}
b := r.s[r.i];
r.i += 1;
return b, nil;
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;
return .Invalid_Unread
}
r.prev_rune = -1;
r.i -= 1;
return nil;
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 = -1
return 0, 0, .EOF
}
r.prev_rune = int(r.i);
r.prev_rune = int(r.i)
if c := r.s[r.i]; c < utf8.RUNE_SELF {
r.i += 1;
return rune(c), 1, nil;
r.i += 1
return rune(c), 1, nil
}
ch, size = utf8.decode_rune(r.s[r.i:]);
r.i += i64(size);
return;
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;
return .Invalid_Unread
}
if r.prev_rune < 0 {
return .Invalid_Unread;
return .Invalid_Unread
}
r.i = i64(r.prev_rune);
r.prev_rune = -1;
return nil;
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;
r.prev_rune = -1
abs: i64
switch whence {
case .Start:
abs = offset;
abs = offset
case .Current:
abs = r.i + offset;
abs = r.i + offset
case .End:
abs = i64(len(r.s)) + offset;
abs = i64(len(r.s)) + offset
case:
return 0, .Invalid_Whence;
return 0, .Invalid_Whence
}
if abs < 0 {
return 0, .Invalid_Offset;
return 0, .Invalid_Offset
}
r.i = abs;
return abs, nil;
r.i = abs
return abs, nil
}
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
r.prev_rune = -1;
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
return 0, nil;
return 0, nil
}
s := r.s[r.i:];
m: int;
m, err = io.write(w, s);
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");
panic("bytes.Reader.write_to: invalid io.write_string count")
}
r.i += i64(m);
n = i64(m);
r.i += i64(m)
n = i64(m)
if m != len(s) && err == nil {
err = .Short_Write;
err = .Short_Write
}
return;
return
}
@(private)
_reader_vtable := &io.Stream_VTable{
impl_size = proc(s: io.Stream) -> i64 {
r := (^Reader)(s.stream_data);
return reader_size(r);
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);
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);
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);
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);
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);
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);
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);
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);
r := (^Reader)(s.stream_data)
return reader_write_to(r, w)
},
};
}
+25 -25
View File
@@ -2,34 +2,34 @@ package c
import b "core:builtin"
CHAR_BIT :: 8;
CHAR_BIT :: 8
bool :: b.bool;
char :: b.u8;
byte :: b.byte;
schar :: b.i8;
uchar :: b.u8;
short :: b.i16;
ushort :: b.u16;
int :: b.i32;
uint :: b.u32;
bool :: b.bool
char :: b.u8
byte :: b.byte
schar :: b.i8
uchar :: b.u8
short :: b.i16
ushort :: b.u16
int :: b.i32
uint :: b.u32
long :: b.i32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.i64;
ulong :: b.u32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.u64;
long :: b.i32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.i64
ulong :: b.u32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.u64
longlong :: b.i64;
ulonglong :: b.u64;
float :: b.f32;
double :: b.f64;
complex_float :: b.complex64;
complex_double :: b.complex128;
longlong :: b.i64
ulonglong :: b.u64
float :: b.f32
double :: b.f64
complex_float :: b.complex64
complex_double :: b.complex128
#assert(size_of(b.uintptr) == size_of(b.int));
#assert(size_of(b.uintptr) == size_of(b.int))
size_t :: b.uint;
ssize_t :: b.int;
ptrdiff_t :: b.int;
uintptr_t :: b.uintptr;
intptr_t :: b.int;
size_t :: b.uint
ssize_t :: b.int
ptrdiff_t :: b.int
uintptr_t :: b.uintptr
intptr_t :: b.int
wchar_t :: b.u16 when (ODIN_OS == "windows") else b.u32;
wchar_t :: b.u16 when (ODIN_OS == "windows") else b.u32
+7 -7
View File
@@ -6,20 +6,20 @@ const_expr :: proc(rest: ^^Token, tok: ^Token) -> i64 {
// TODO(bill): Handle const_expr correctly
// This is effectively a mini-parser
assert(rest != nil);
assert(tok != nil);
rest^ = tokenizer.new_eof(tok);
assert(rest != nil)
assert(tok != nil)
rest^ = tokenizer.new_eof(tok)
switch v in tok.val {
case i64:
return v;
return v
case f64:
return i64(v);
return i64(v)
case string:
return 0;
return 0
case []u16:
// TODO
case []u32:
// TODO
}
return 0;
return 0
}
File diff suppressed because it is too large Load Diff
+71 -71
View File
@@ -5,150 +5,150 @@ import "core:unicode/utf8"
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
hex_to_int :: proc(c: byte) -> int {
switch c {
case '0'..='9': return int(c-'0');
case 'a'..='f': return int(c-'a')+10;
case 'A'..='F': return int(c-'A')+10;
case '0'..='9': return int(c-'0')
case 'a'..='f': return int(c-'a')+10
case 'A'..='F': return int(c-'A')+10
}
return -1;
return -1
}
w: int;
w: int
if str[0] == quote && quote == '"' {
return;
return
} else if str[0] >= 0x80 {
r, w = utf8.decode_rune_in_string(str);
return r, true, str[w:], true;
r, w = utf8.decode_rune_in_string(str)
return r, true, str[w:], true
} else if str[0] != '\\' {
return rune(str[0]), false, str[1:], true;
return rune(str[0]), false, str[1:], true
}
if len(str) <= 1 {
return;
return
}
s := str;
c := s[1];
s = s[2:];
s := str
c := s[1]
s = s[2:]
switch c {
case: r = rune(c);
case: r = rune(c)
case 'a': r = '\a';
case 'b': r = '\b';
case 'e': r = '\e';
case 'f': r = '\f';
case 'n': r = '\n';
case 'r': r = '\r';
case 't': r = '\t';
case 'v': r = '\v';
case '\\': r = '\\';
case 'a': r = '\a'
case 'b': r = '\b'
case 'e': r = '\e'
case 'f': r = '\f'
case 'n': r = '\n'
case 'r': r = '\r'
case 't': r = '\t'
case 'v': r = '\v'
case '\\': r = '\\'
case '"': r = '"';
case '\'': r = '\'';
case '"': r = '"'
case '\'': r = '\''
case '0'..='7':
v := int(c-'0');
v := int(c-'0')
if len(s) < 2 {
return;
return
}
for i in 0..<len(s) {
d := int(s[i]-'0');
d := int(s[i]-'0')
if d < 0 || d > 7 {
return;
return
}
v = (v<<3) | d;
v = (v<<3) | d
}
s = s[2:];
s = s[2:]
if v > 0xff {
return;
return
}
r = rune(v);
r = rune(v)
case 'x', 'u', 'U':
count: int;
count: int
switch c {
case 'x': count = 2;
case 'u': count = 4;
case 'U': count = 8;
case 'x': count = 2
case 'u': count = 4
case 'U': count = 8
}
if len(s) < count {
return;
return
}
for i in 0..<count {
d := hex_to_int(s[i]);
d := hex_to_int(s[i])
if d < 0 {
return;
return
}
r = (r<<4) | rune(d);
r = (r<<4) | rune(d)
}
s = s[count:];
s = s[count:]
if c == 'x' {
break;
break
}
if r > utf8.MAX_RUNE {
return;
return
}
multiple_bytes = true;
multiple_bytes = true
}
success = true;
tail_string = s;
return;
success = true
tail_string = s
return
}
unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
contains_rune :: proc(s: string, r: rune) -> int {
for c, offset in s {
if c == r {
return offset;
return offset
}
}
return -1;
return -1
}
assert(len(lit) >= 2);
assert(len(lit) >= 2)
s := lit;
quote := '"';
s := lit
quote := '"'
if s == `""` {
return "", false, true;
return "", false, true
}
if contains_rune(s, '\n') >= 0 {
return s, false, false;
return s, false, false
}
if contains_rune(s, '\\') < 0 && contains_rune(s, quote) < 0 {
if quote == '"' {
return s, false, true;
return s, false, true
}
}
s = s[1:len(s)-1];
s = s[1:len(s)-1]
buf_len := 3*len(s) / 2;
buf := make([]byte, buf_len, allocator);
offset := 0;
buf_len := 3*len(s) / 2
buf := make([]byte, buf_len, allocator)
offset := 0
for len(s) > 0 {
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote));
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote))
if !ok {
delete(buf);
return s, false, false;
delete(buf)
return s, false, false
}
s = tail_string;
s = tail_string
if r < 0x80 || !multiple_bytes {
buf[offset] = byte(r);
offset += 1;
buf[offset] = byte(r)
offset += 1
} else {
b, w := utf8.encode_rune(r);
copy(buf[offset:], b[:w]);
offset += w;
b, w := utf8.encode_rune(r)
copy(buf[offset:], b[:w])
offset += w
}
}
new_string := string(buf[:offset]);
new_string := string(buf[:offset])
return new_string, true, true;
return new_string, true, true
}
+24 -24
View File
@@ -11,58 +11,58 @@ Hide_Set :: struct {
new_hide_set :: proc(name: string) -> ^Hide_Set {
hs := new(Hide_Set);
hs.name = name;
return hs;
hs := new(Hide_Set)
hs.name = name
return hs
}
hide_set_contains :: proc(hs: ^Hide_Set, name: string) -> bool {
for h := hs; h != nil; h = h.next {
if h.name == name {
return true;
return true
}
}
return false;
return false
}
hide_set_union :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
head: Hide_Set;
curr := &head;
head: Hide_Set
curr := &head
for h := a; h != nil; h = h.next {
curr.next = new_hide_set(h.name);
curr = curr.next;
curr.next = new_hide_set(h.name)
curr = curr.next
}
curr.next = b;
return head.next;
curr.next = b
return head.next
}
hide_set_intersection :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
head: Hide_Set;
curr := &head;
head: Hide_Set
curr := &head
for h := a; h != nil; h = h.next {
if hide_set_contains(b, h.name) {
curr.next = new_hide_set(h.name);
curr = curr.next;
curr.next = new_hide_set(h.name)
curr = curr.next
}
}
return head.next;
return head.next
}
add_hide_set :: proc(tok: ^Token, hs: ^Hide_Set) -> ^Token {
head: Token;
curr := &head;
head: Token
curr := &head
tok := tok;
tok := tok
for ; tok != nil; tok = tok.next {
t := copy_token(tok);
t.hide_set = hide_set_union(t.hide_set, hs);
curr.next = t;
curr = curr.next;
t := copy_token(tok)
t.hide_set = hide_set_union(t.hide_set, hs)
curr.next = t
curr = curr.next
}
return head.next;
return head.next
}
+13 -13
View File
@@ -80,29 +80,29 @@ Token :: struct {
origin: ^Token,
}
Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool;
Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool
copy_token :: proc(tok: ^Token) -> ^Token {
t := new_clone(tok^);
t.next = nil;
return t;
t := new_clone(tok^)
t.next = nil
return t
}
new_eof :: proc(tok: ^Token) -> ^Token {
t := new_clone(tok^);
t.kind = .EOF;
t.lit = "";
return t;
t := new_clone(tok^)
t.kind = .EOF
t.lit = ""
return t
}
default_is_keyword :: proc(tok: ^Token) -> bool {
if tok.kind == .Keyword {
return true;
return true
}
if len(tok.lit) > 0 {
return default_keyword_set[tok.lit];
return default_keyword_set[tok.lit]
}
return false;
return false
}
@@ -117,7 +117,7 @@ token_name := [Token_Kind]string {
.PP_Number = "preprocessor number",
.Comment = "comment",
.EOF = "eof",
};
}
default_keyword_set := map[string]bool{
"auto" = true,
@@ -166,4 +166,4 @@ default_keyword_set := map[string]bool{
"__restrict__" = true,
"__thread" = true,
"__attribute__" = true,
};
}
+267 -267
View File
@@ -6,7 +6,7 @@ import "core:strings"
import "core:unicode/utf8"
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any)
Tokenizer :: struct {
@@ -34,415 +34,415 @@ Tokenizer :: struct {
}
init_defaults :: proc(t: ^Tokenizer, err: Error_Handler = default_error_handler, warn: Error_Handler = default_warn_handler) {
t.err = err;
t.warn = warn;
t.err = err
t.warn = warn
}
@(private)
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> (pos: Pos) {
pos.file = t.path;
pos.offset = offset;
pos.line = t.line_count;
pos.column = offset - t.line_offset + 1;
return;
pos.file = t.path
pos.offset = offset
pos.line = t.line_count
pos.column = offset - t.line_offset + 1
return
}
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column);
fmt.eprintf(msg, ..args);
fmt.eprintf("\n");
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column)
fmt.eprintf(msg, ..args)
fmt.eprintf("\n")
}
default_warn_handler :: proc(pos: Pos, msg: string, args: ..any) {
fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column);
fmt.eprintf(msg, ..args);
fmt.eprintf("\n");
fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column)
fmt.eprintf(msg, ..args)
fmt.eprintf("\n")
}
error_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
pos := offset_to_pos(t, offset);
pos := offset_to_pos(t, offset)
if t.err != nil {
t.err(pos, msg, ..args);
t.err(pos, msg, ..args)
}
t.error_count += 1;
t.error_count += 1
}
warn_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
pos := offset_to_pos(t, offset);
pos := offset_to_pos(t, offset)
if t.warn != nil {
t.warn(pos, msg, ..args);
t.warn(pos, msg, ..args)
}
t.warning_count += 1;
t.warning_count += 1
}
error :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
pos := tok.pos;
pos := tok.pos
if t.err != nil {
t.err(pos, msg, ..args);
t.err(pos, msg, ..args)
}
t.error_count += 1;
t.error_count += 1
}
warn :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
pos := tok.pos;
pos := tok.pos
if t.warn != nil {
t.warn(pos, msg, ..args);
t.warn(pos, msg, ..args)
}
t.warning_count += 1;
t.warning_count += 1
}
advance_rune :: proc(t: ^Tokenizer) {
if t.read_offset < len(t.src) {
t.offset = t.read_offset;
t.offset = t.read_offset
if t.ch == '\n' {
t.at_bol = true;
t.line_offset = t.offset;
t.line_count += 1;
t.at_bol = true
t.line_offset = t.offset
t.line_count += 1
}
r, w := rune(t.src[t.read_offset]), 1;
r, w := rune(t.src[t.read_offset]), 1
switch {
case r == 0:
error_offset(t, t.offset, "illegal character NUL");
error_offset(t, t.offset, "illegal character NUL")
case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune(t.src[t.read_offset:]);
r, w = utf8.decode_rune(t.src[t.read_offset:])
if r == utf8.RUNE_ERROR && w == 1 {
error_offset(t, t.offset, "illegal UTF-8 encoding");
error_offset(t, t.offset, "illegal UTF-8 encoding")
} else if r == utf8.RUNE_BOM && t.offset > 0 {
error_offset(t, t.offset, "illegal byte order mark");
error_offset(t, t.offset, "illegal byte order mark")
}
}
t.read_offset += w;
t.ch = r;
t.read_offset += w
t.ch = r
} else {
t.offset = len(t.src);
t.offset = len(t.src)
if t.ch == '\n' {
t.at_bol = true;
t.line_offset = t.offset;
t.line_count += 1;
t.at_bol = true
t.line_offset = t.offset
t.line_count += 1
}
t.ch = -1;
t.ch = -1
}
}
advance_rune_n :: proc(t: ^Tokenizer, n: int) {
for in 0..<n {
advance_rune(t);
advance_rune(t)
}
}
is_digit :: proc(r: rune) -> bool {
return '0' <= r && r <= '9';
return '0' <= r && r <= '9'
}
skip_whitespace :: proc(t: ^Tokenizer) {
for {
switch t.ch {
case ' ', '\t', '\r', '\v', '\f', '\n':
t.has_space = true;
advance_rune(t);
t.has_space = true
advance_rune(t)
case:
return;
return
}
}
}
scan_comment :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
next := -1;
offset := t.offset-1
next := -1
general: {
if t.ch == '/'{ // line comments
advance_rune(t);
advance_rune(t)
for t.ch != '\n' && t.ch >= 0 {
advance_rune(t);
advance_rune(t)
}
next = t.offset;
next = t.offset
if t.ch == '\n' {
next += 1;
next += 1
}
break general;
break general
}
/* style comment */
advance_rune(t);
advance_rune(t)
for t.ch >= 0 {
ch := t.ch;
advance_rune(t);
ch := t.ch
advance_rune(t)
if ch == '*' && t.ch == '/' {
advance_rune(t);
next = t.offset;
break general;
advance_rune(t)
next = t.offset
break general
}
}
error_offset(t, offset, "comment not terminated");
error_offset(t, offset, "comment not terminated")
}
lit := t.src[offset : t.offset];
lit := t.src[offset : t.offset]
// NOTE(bill): Strip CR for line comments
for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
lit = lit[:len(lit)-1];
lit = lit[:len(lit)-1]
}
return string(lit);
return string(lit)
}
scan_identifier :: proc(t: ^Tokenizer) -> string {
offset := t.offset;
offset := t.offset
for is_ident1(t.ch) {
advance_rune(t);
advance_rune(t)
}
return string(t.src[offset : t.offset]);
return string(t.src[offset : t.offset])
}
scan_string :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
offset := t.offset-1
for {
ch := t.ch;
ch := t.ch
if ch == '\n' || ch < 0 {
error_offset(t, offset, "string literal was not terminated");
break;
error_offset(t, offset, "string literal was not terminated")
break
}
advance_rune(t);
advance_rune(t)
if ch == '"' {
break;
break
}
if ch == '\\' {
scan_escape(t);
scan_escape(t)
}
}
return string(t.src[offset : t.offset]);
return string(t.src[offset : t.offset])
}
digit_val :: proc(r: rune) -> int {
switch r {
case '0'..='9':
return int(r-'0');
return int(r-'0')
case 'A'..='F':
return int(r-'A' + 10);
return int(r-'A' + 10)
case 'a'..='f':
return int(r-'a' + 10);
return int(r-'a' + 10)
}
return 16;
return 16
}
scan_escape :: proc(t: ^Tokenizer) -> bool {
offset := t.offset;
offset := t.offset
esc := t.ch;
n: int;
base, max: u32;
esc := t.ch
n: int
base, max: u32
switch esc {
case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '"':
advance_rune(t);
return true;
advance_rune(t)
return true
case '0'..='7':
for digit_val(t.ch) < 8 {
advance_rune(t);
advance_rune(t)
}
return true;
return true
case 'x':
advance_rune(t);
advance_rune(t)
for digit_val(t.ch) < 16 {
advance_rune(t);
advance_rune(t)
}
return true;
return true
case 'u':
advance_rune(t);
n, base, max = 4, 16, utf8.MAX_RUNE;
advance_rune(t)
n, base, max = 4, 16, utf8.MAX_RUNE
case 'U':
advance_rune(t);
n, base, max = 8, 16, utf8.MAX_RUNE;
advance_rune(t)
n, base, max = 8, 16, utf8.MAX_RUNE
case:
if t.ch < 0 {
error_offset(t, offset, "escape sequence was not terminated");
error_offset(t, offset, "escape sequence was not terminated")
} else {
break;
break
}
return false;
return false
}
x: u32;
x: u32
main_loop: for n > 0 {
d := u32(digit_val(t.ch));
d := u32(digit_val(t.ch))
if d >= base {
if t.ch == '"' || t.ch == '\'' {
break main_loop;
break main_loop
}
if t.ch < 0 {
error_offset(t, t.offset, "escape sequence was not terminated");
error_offset(t, t.offset, "escape sequence was not terminated")
} else {
error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch);
error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch)
}
return false;
return false
}
x = x*base + d;
advance_rune(t);
n -= 1;
x = x*base + d
advance_rune(t)
n -= 1
}
if x > max || 0xd800 <= x && x <= 0xe000 {
error_offset(t, offset, "escape sequence is an invalid Unicode code point");
return false;
error_offset(t, offset, "escape sequence is an invalid Unicode code point")
return false
}
return true;
return true
}
scan_rune :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
valid := true;
n := 0;
offset := t.offset-1
valid := true
n := 0
for {
ch := t.ch;
ch := t.ch
if ch == '\n' || ch < 0 {
if valid {
error_offset(t, offset, "rune literal not terminated");
valid = false;
error_offset(t, offset, "rune literal not terminated")
valid = false
}
break;
break
}
advance_rune(t);
advance_rune(t)
if ch == '\'' {
break;
break
}
n += 1;
n += 1
if ch == '\\' {
if !scan_escape(t) {
valid = false;
valid = false
}
}
}
if valid && n != 1 {
error_offset(t, offset, "illegal rune literal");
error_offset(t, offset, "illegal rune literal")
}
return string(t.src[offset : t.offset]);
return string(t.src[offset : t.offset])
}
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
for digit_val(t.ch) < base {
advance_rune(t);
advance_rune(t)
}
}
scan_exponent :: proc(t: ^Tokenizer) {
if t.ch == 'e' || t.ch == 'E' || t.ch == 'p' || t.ch == 'P' {
advance_rune(t);
advance_rune(t)
if t.ch == '-' || t.ch == '+' {
advance_rune(t);
advance_rune(t)
}
if digit_val(t.ch) < 10 {
scan_mantissa(t, 10);
scan_mantissa(t, 10)
} else {
error_offset(t, t.offset, "illegal floating-point exponent");
error_offset(t, t.offset, "illegal floating-point exponent")
}
}
}
scan_fraction :: proc(t: ^Tokenizer) -> (early_exit: bool) {
if t.ch == '.' && peek(t) == '.' {
return true;
return true
}
if t.ch == '.' {
advance_rune(t);
scan_mantissa(t, 10);
advance_rune(t)
scan_mantissa(t, 10)
}
return false;
return false
}
check_end := true;
check_end := true
offset := t.offset;
seen_point := seen_decimal_point;
offset := t.offset
seen_point := seen_decimal_point
if seen_point {
offset -= 1;
scan_mantissa(t, 10);
scan_exponent(t);
offset -= 1
scan_mantissa(t, 10)
scan_exponent(t)
} else {
if t.ch == '0' {
int_base :: proc(t: ^Tokenizer, base: int, msg: string) {
prev := t.offset;
advance_rune(t);
scan_mantissa(t, base);
prev := t.offset
advance_rune(t)
scan_mantissa(t, base)
if t.offset - prev <= 1 {
error_offset(t, t.offset, msg);
error_offset(t, t.offset, msg)
}
}
advance_rune(t);
advance_rune(t)
switch t.ch {
case 'b', 'B':
int_base(t, 2, "illegal binary integer");
int_base(t, 2, "illegal binary integer")
case 'x', 'X':
int_base(t, 16, "illegal hexadecimal integer");
int_base(t, 16, "illegal hexadecimal integer")
case:
seen_point = false;
scan_mantissa(t, 10);
seen_point = false
scan_mantissa(t, 10)
if t.ch == '.' {
seen_point = true;
seen_point = true
if scan_fraction(t) {
check_end = false;
check_end = false
}
}
if check_end {
scan_exponent(t);
check_end = false;
scan_exponent(t)
check_end = false
}
}
}
}
if check_end {
scan_mantissa(t, 10);
scan_mantissa(t, 10)
if !scan_fraction(t) {
scan_exponent(t);
scan_exponent(t)
}
}
return .Number, string(t.src[offset : t.offset]);
return .Number, string(t.src[offset : t.offset])
}
scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
kind = .Punct;
kind = .Punct
switch ch {
case:
kind = .Invalid;
kind = .Invalid
case '<', '>':
if t.ch == ch {
advance_rune(t);
advance_rune(t)
}
if t.ch == '=' {
advance_rune(t);
advance_rune(t)
}
case '!', '+', '-', '*', '/', '%', '^', '=':
if t.ch == '=' {
advance_rune(t);
advance_rune(t)
}
case '#':
if t.ch == '#' {
advance_rune(t);
advance_rune(t)
}
case '&':
if t.ch == '=' || t.ch == '&' {
advance_rune(t);
advance_rune(t)
}
case '|':
if t.ch == '=' || t.ch == '|' {
advance_rune(t);
advance_rune(t)
}
case '(', ')', '[', ']', '{', '}':
// okay
@@ -452,216 +452,216 @@ scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
// okay
case '.':
if t.ch == '.' && peek(t) == '.' {
advance_rune(t);
advance_rune(t); // consume last '.'
advance_rune(t)
advance_rune(t) // consume last '.'
}
}
return;
return
}
peek :: proc(t: ^Tokenizer) -> byte {
if t.read_offset < len(t.src) {
return t.src[t.read_offset];
return t.src[t.read_offset]
}
return 0;
return 0
}
peek_str :: proc(t: ^Tokenizer, str: string) -> bool {
if t.read_offset < len(t.src) {
return strings.has_prefix(string(t.src[t.offset:]), str);
return strings.has_prefix(string(t.src[t.offset:]), str)
}
return false;
return false
}
scan_literal_prefix :: proc(t: ^Tokenizer, str: string, prefix: ^string) -> bool {
if peek_str(t, str) {
offset := t.offset;
offset := t.offset
for _ in str {
advance_rune(t);
advance_rune(t)
}
prefix^ = string(t.src[offset:][:len(str)-1]);
return true;
prefix^ = string(t.src[offset:][:len(str)-1])
return true
}
return false;
return false
}
allow_next_to_be_newline :: proc(t: ^Tokenizer) -> bool {
if t.ch == '\n' {
advance_rune(t);
return true;
advance_rune(t)
return true
} else if t.ch == '\r' && peek(t) == '\n' { // allow for MS-DOS style line endings
advance_rune(t); // \r
advance_rune(t); // \n
return true;
advance_rune(t) // \r
advance_rune(t) // \n
return true
}
return false;
return false
}
scan :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
skip_whitespace(t);
skip_whitespace(t)
offset := t.offset;
offset := t.offset
kind: Token_Kind;
lit: string;
prefix: string;
kind: Token_Kind
lit: string
prefix: string
switch ch := t.ch; {
case scan_literal_prefix(t, `u8"`, &prefix):
kind = .String;
lit = scan_string(t);
kind = .String
lit = scan_string(t)
case scan_literal_prefix(t, `u"`, &prefix):
kind = .String;
lit = scan_string(t);
kind = .String
lit = scan_string(t)
case scan_literal_prefix(t, `L"`, &prefix):
kind = .String;
lit = scan_string(t);
kind = .String
lit = scan_string(t)
case scan_literal_prefix(t, `U"`, &prefix):
kind = .String;
lit = scan_string(t);
kind = .String
lit = scan_string(t)
case scan_literal_prefix(t, `u'`, &prefix):
kind = .Char;
lit = scan_rune(t);
kind = .Char
lit = scan_rune(t)
case scan_literal_prefix(t, `L'`, &prefix):
kind = .Char;
lit = scan_rune(t);
kind = .Char
lit = scan_rune(t)
case scan_literal_prefix(t, `U'`, &prefix):
kind = .Char;
lit = scan_rune(t);
kind = .Char
lit = scan_rune(t)
case is_ident0(ch):
lit = scan_identifier(t);
kind = .Ident;
lit = scan_identifier(t)
kind = .Ident
case '0' <= ch && ch <= '9':
kind, lit = scan_number(t, false);
kind, lit = scan_number(t, false)
case:
advance_rune(t);
advance_rune(t)
switch ch {
case -1:
kind = .EOF;
kind = .EOF
case '\\':
kind = .Punct;
kind = .Punct
if allow_next_to_be_newline(t) {
t.at_bol = true;
t.has_space = false;
return scan(t, f);
t.at_bol = true
t.has_space = false
return scan(t, f)
}
case '.':
if is_digit(t.ch) {
kind, lit = scan_number(t, true);
kind, lit = scan_number(t, true)
} else {
kind = scan_punct(t, ch);
kind = scan_punct(t, ch)
}
case '"':
kind = .String;
lit = scan_string(t);
kind = .String
lit = scan_string(t)
case '\'':
kind = .Char;
lit = scan_rune(t);
kind = .Char
lit = scan_rune(t)
case '/':
if t.ch == '/' || t.ch == '*' {
kind = .Comment;
lit = scan_comment(t);
t.has_space = true;
break;
kind = .Comment
lit = scan_comment(t)
t.has_space = true
break
}
fallthrough;
fallthrough
case:
kind = scan_punct(t, ch);
kind = scan_punct(t, ch)
if kind == .Invalid && ch != utf8.RUNE_BOM {
error_offset(t, t.offset, "illegal character '%r': %d", ch, ch);
error_offset(t, t.offset, "illegal character '%r': %d", ch, ch)
}
}
}
if lit == "" {
lit = string(t.src[offset : t.offset]);
lit = string(t.src[offset : t.offset])
}
if kind == .Comment {
return scan(t, f);
return scan(t, f)
}
tok := new(Token);
tok.kind = kind;
tok.lit = lit;
tok.pos = offset_to_pos(t, offset);
tok.file = f;
tok.prefix = prefix;
tok.at_bol = t.at_bol;
tok.has_space = t.has_space;
tok := new(Token)
tok.kind = kind
tok.lit = lit
tok.pos = offset_to_pos(t, offset)
tok.file = f
tok.prefix = prefix
tok.at_bol = t.at_bol
tok.has_space = t.has_space
t.at_bol, t.has_space = false, false;
t.at_bol, t.has_space = false, false
return tok;
return tok
}
tokenize :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
setup_tokenizer: {
t.src = f.src;
t.ch = ' ';
t.offset = 0;
t.read_offset = 0;
t.line_offset = 0;
t.line_count = len(t.src) > 0 ? 1 : 0;
t.error_count = 0;
t.path = f.name;
t.src = f.src
t.ch = ' '
t.offset = 0
t.read_offset = 0
t.line_offset = 0
t.line_count = len(t.src) > 0 ? 1 : 0
t.error_count = 0
t.path = f.name
advance_rune(t);
advance_rune(t)
if t.ch == utf8.RUNE_BOM {
advance_rune(t);
advance_rune(t)
}
}
t.at_bol = true;
t.has_space = false;
t.at_bol = true
t.has_space = false
head: Token;
curr := &head;
head: Token
curr := &head
for {
tok := scan(t, f);
tok := scan(t, f)
if tok == nil {
break;
break
}
curr.next = tok;
curr = curr.next;
curr.next = tok
curr = curr.next
if tok.kind == .EOF {
break;
break
}
}
return head.next;
return head.next
}
add_new_file :: proc(t: ^Tokenizer, name: string, src: []byte, id: int) -> ^File {
file := new(File);
file.id = id;
file.src = src;
file.name = name;
file.display_name = name;
return file;
file := new(File)
file.id = id
file.src = src
file.name = name
file.display_name = name
return file
}
tokenize_file :: proc(t: ^Tokenizer, path: string, id: int, loc := #caller_location) -> ^Token {
src, ok := os.read_entire_file(path);
src, ok := os.read_entire_file(path)
if !ok {
return nil;
return nil
}
return tokenize(t, add_new_file(t, path, src, id));
return tokenize(t, add_new_file(t, path, src, id))
}
inline_tokenize :: proc(t: ^Tokenizer, tok: ^Token, src: []byte) -> ^Token {
file := new(File);
file.src = src;
file := new(File)
file.src = src
if tok.file != nil {
file.id = tok.file.id;
file.name = tok.file.name;
file.display_name = tok.file.name;
file.id = tok.file.id
file.name = tok.file.name
file.display_name = tok.file.name
}
return tokenize(t, file);
return tokenize(t, file)
}
+13 -13
View File
@@ -4,10 +4,10 @@ package c_frontend_tokenizer
in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
for i := 0; range[i] != -1; i += 2 {
if range[i] <= c && c <= range[i+1] {
return true;
return true
}
}
return false;
return false
}
@@ -15,11 +15,11 @@ in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
//
// is_ident0 returns true if a given character is acceptable as the first character of an identifier.
is_ident0 :: proc(c: rune) -> bool {
return in_range(_range_ident0, c);
return in_range(_range_ident0, c)
}
// is_ident0 returns true if a given character is acceptable as a non-first character of an identifier.
is_ident1 :: proc(c: rune) -> bool {
return is_ident0(c) || in_range(_range_ident1, c);
return is_ident0(c) || in_range(_range_ident1, c)
}
// Returns the number of columns needed to display a given character in a fixed-width font.
@@ -27,18 +27,18 @@ is_ident1 :: proc(c: rune) -> bool {
char_width :: proc(c: rune) -> int {
switch {
case in_range(_range_width0, c):
return 0;
return 0
case in_range(_range_width2, c):
return 2;
return 2
}
return 1;
return 1
}
display_width :: proc(str: string) -> (w: int) {
for c in str {
w += char_width(c);
w += char_width(c)
}
return;
return
}
@@ -59,12 +59,12 @@ _range_ident0 := []rune{
0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD,
-1,
};
}
_range_ident1 := []rune{
'0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F,
-1,
};
}
_range_width0 := []rune{
@@ -105,7 +105,7 @@ _range_width0 := []rune{
0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
-1,
};
}
_range_width2 := []rune{
0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
@@ -113,4 +113,4 @@ _range_width2 := []rune{
0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
0x20000, 0x2FFFD, 0x30000, 0x3FFFD,
-1,
};
}
+103 -103
View File
@@ -22,7 +22,7 @@ import "core:bytes"
When a decompression routine doesn't stream its output, but writes to a buffer,
we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
*/
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20));
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20))
/*
This bounds the maximum a buffer will resize to as needed, or the maximum we'll
@@ -36,12 +36,12 @@ when size_of(uintptr) == 8 {
For 64-bit platforms, we set the default max buffer size to 4 GiB,
which is GZIP and PKZIP's max payload size.
*/
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32));
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
} else {
/*
For 32-bit platforms, we set the default max buffer size to 512 MiB.
*/
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29));
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
}
@@ -141,7 +141,7 @@ Context_Memory_Input :: struct #packed {
size_packed: i64,
size_unpacked: i64,
}
#assert(size_of(Context_Memory_Input) == 64);
#assert(size_of(Context_Memory_Input) == 64)
Context_Stream_Input :: struct #packed {
input_data: []u8,
@@ -179,78 +179,78 @@ Context_Stream_Input :: struct #packed {
// TODO: Make these return compress.Error errors.
input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Error) {
return i64(len(z.input_data)), nil;
return i64(len(z.input_data)), nil
}
input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Error) {
return io.size(z.input), nil;
return io.size(z.input), nil
}
input_size :: proc{input_size_from_memory, input_size_from_stream};
input_size :: proc{input_size_from_memory, input_size_from_stream}
@(optimization_mode="speed")
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
#no_bounds_check {
if len(z.input_data) >= size {
res = z.input_data[:size];
z.input_data = z.input_data[size:];
return res, .None;
res = z.input_data[:size]
z.input_data = z.input_data[size:]
return res, .None
}
}
if len(z.input_data) == 0 {
return []u8{}, .EOF;
return []u8{}, .EOF
} else {
return []u8{}, .Short_Buffer;
return []u8{}, .Short_Buffer
}
}
@(optimization_mode="speed")
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
b := make([]u8, size, context.temp_allocator);
_, e := z.input->impl_read(b[:]);
b := make([]u8, size, context.temp_allocator)
_, e := z.input->impl_read(b[:])
if e == .None {
return b, .None;
return b, .None
}
return []u8{}, e;
return []u8{}, e
}
read_slice :: proc{read_slice_from_memory, read_slice_from_stream};
read_slice :: proc{read_slice_from_memory, read_slice_from_stream}
@(optimization_mode="speed")
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
b, e := read_slice(z, size_of(T));
b, e := read_slice(z, size_of(T))
if e == .None {
return (^T)(&b[0])^, .None;
return (^T)(&b[0])^, .None
}
return T{}, e;
return T{}, e
}
@(optimization_mode="speed")
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
#no_bounds_check {
if len(z.input_data) >= 1 {
res = z.input_data[0];
z.input_data = z.input_data[1:];
return res, .None;
res = z.input_data[0]
z.input_data = z.input_data[1:]
return res, .None
}
}
return 0, .EOF;
return 0, .EOF
}
@(optimization_mode="speed")
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
b, e := read_slice_from_stream(z, 1);
b, e := read_slice_from_stream(z, 1)
if e == .None {
return b[0], .None;
return b[0], .None
}
return 0, e;
return 0, e
}
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream};
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
/*
You would typically only use this at the end of Inflate, to drain bits from the code buffer
@@ -259,64 +259,64 @@ read_u8 :: proc{read_u8_from_memory, read_u8_from_stream};
@(optimization_mode="speed")
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
if z.num_bits >= 8 {
res = u8(read_bits_no_refill_lsb(z, 8));
res = u8(read_bits_no_refill_lsb(z, 8))
} else {
size, _ := input_size(z);
size, _ := input_size(z)
if size > 0 {
res, err = read_u8(z);
res, err = read_u8(z)
} else {
err = .EOF;
err = .EOF
}
}
return;
return
}
@(optimization_mode="speed")
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
size :: size_of(T);
size :: size_of(T)
#no_bounds_check {
if len(z.input_data) >= size {
buf := z.input_data[:size];
return (^T)(&buf[0])^, .None;
buf := z.input_data[:size]
return (^T)(&buf[0])^, .None
}
}
if len(z.input_data) == 0 {
return T{}, .EOF;
return T{}, .EOF
} else {
return T{}, .Short_Buffer;
return T{}, .Short_Buffer
}
}
@(optimization_mode="speed")
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
size :: size_of(T);
size :: size_of(T)
// Get current position to read from.
curr, e1 := z.input->impl_seek(0, .Current);
curr, e1 := z.input->impl_seek(0, .Current)
if e1 != .None {
return T{}, e1;
return T{}, e1
}
r, e2 := io.to_reader_at(z.input);
r, e2 := io.to_reader_at(z.input)
if !e2 {
return T{}, .Empty;
return T{}, .Empty
}
when size <= 128 {
b: [size]u8;
b: [size]u8
} else {
b := make([]u8, size, context.temp_allocator);
b := make([]u8, size, context.temp_allocator)
}
_, e3 := io.read_at(r, b[:], curr);
_, e3 := io.read_at(r, b[:], curr)
if e3 != .None {
return T{}, .Empty;
return T{}, .Empty
}
res = (^T)(&b[0])^;
return res, .None;
res = (^T)(&b[0])^
return res, .None
}
peek_data :: proc{peek_data_from_memory, peek_data_from_stream};
peek_data :: proc{peek_data_from_memory, peek_data_from_stream}
@@ -324,31 +324,31 @@ peek_data :: proc{peek_data_from_memory, peek_data_from_stream};
@(optimization_mode="speed")
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
// Look back into the sliding window.
return z.output.buf[z.bytes_written - offset], .None;
return z.output.buf[z.bytes_written - offset], .None
}
// Generalized bit reader LSB
@(optimization_mode="speed")
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
refill := u64(width);
b := u64(0);
refill := u64(width)
b := u64(0)
if z.num_bits > refill {
return;
return
}
for {
if len(z.input_data) != 0 {
b = u64(z.input_data[0]);
z.input_data = z.input_data[1:];
b = u64(z.input_data[0])
z.input_data = z.input_data[1:]
} else {
b = 0;
b = 0
}
z.code_buffer |= b << u8(z.num_bits);
z.num_bits += 8;
z.code_buffer |= b << u8(z.num_bits)
z.num_bits += 8
if z.num_bits > refill {
break;
break
}
}
}
@@ -356,123 +356,123 @@ refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width :=
// Generalized bit reader LSB
@(optimization_mode="speed")
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
refill := u64(width);
refill := u64(width)
for {
if z.num_bits > refill {
break;
break
}
if z.code_buffer == 0 && z.num_bits > 63 {
z.num_bits = 0;
z.num_bits = 0
}
if z.code_buffer >= 1 << uint(z.num_bits) {
// Code buffer is malformed.
z.num_bits = max(u64);
return;
z.num_bits = max(u64)
return
}
b, err := read_u8(z);
b, err := read_u8(z)
if err != .None {
// This is fine at the end of the file.
return;
return
}
z.code_buffer |= (u64(b) << u8(z.num_bits));
z.num_bits += 8;
z.code_buffer |= (u64(b) << u8(z.num_bits))
z.num_bits += 8
}
}
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream};
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream}
@(optimization_mode="speed")
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
z.code_buffer >>= width;
z.num_bits -= u64(width);
z.code_buffer >>= width
z.num_bits -= u64(width)
}
@(optimization_mode="speed")
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
z.code_buffer >>= width;
z.num_bits -= u64(width);
z.code_buffer >>= width
z.num_bits -= u64(width)
}
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream};
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream}
@(optimization_mode="speed")
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
if z.num_bits < u64(width) {
refill_lsb(z);
refill_lsb(z)
}
return u32(z.code_buffer & ~(~u64(0) << width));
return u32(z.code_buffer & ~(~u64(0) << width))
}
@(optimization_mode="speed")
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
if z.num_bits < u64(width) {
refill_lsb(z);
refill_lsb(z)
}
return u32(z.code_buffer & ~(~u64(0) << width));
return u32(z.code_buffer & ~(~u64(0) << width))
}
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream};
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream}
@(optimization_mode="speed")
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
assert(z.num_bits >= u64(width));
return u32(z.code_buffer & ~(~u64(0) << width));
assert(z.num_bits >= u64(width))
return u32(z.code_buffer & ~(~u64(0) << width))
}
@(optimization_mode="speed")
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
assert(z.num_bits >= u64(width));
return u32(z.code_buffer & ~(~u64(0) << width));
assert(z.num_bits >= u64(width))
return u32(z.code_buffer & ~(~u64(0) << width))
}
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream};
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream}
@(optimization_mode="speed")
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
k := #force_inline peek_bits_lsb(z, width);
#force_inline consume_bits_lsb(z, width);
return k;
k := #force_inline peek_bits_lsb(z, width)
#force_inline consume_bits_lsb(z, width)
return k
}
@(optimization_mode="speed")
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
k := peek_bits_lsb(z, width);
consume_bits_lsb(z, width);
return k;
k := peek_bits_lsb(z, width)
consume_bits_lsb(z, width)
return k
}
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream};
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream}
@(optimization_mode="speed")
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
k := #force_inline peek_bits_no_refill_lsb(z, width);
#force_inline consume_bits_lsb(z, width);
return k;
k := #force_inline peek_bits_no_refill_lsb(z, width)
#force_inline consume_bits_lsb(z, width)
return k
}
@(optimization_mode="speed")
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
k := peek_bits_no_refill_lsb(z, width);
consume_bits_lsb(z, width);
return k;
k := peek_bits_no_refill_lsb(z, width)
consume_bits_lsb(z, width)
return k
}
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream};
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream}
@(optimization_mode="speed")
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
discard := u8(z.num_bits & 7);
#force_inline consume_bits_lsb(z, discard);
discard := u8(z.num_bits & 7)
#force_inline consume_bits_lsb(z, discard)
}
@(optimization_mode="speed")
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
discard := u8(z.num_bits & 7);
consume_bits_lsb(z, discard);
discard := u8(z.num_bits & 7)
consume_bits_lsb(z, discard)
}
discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream};
+28 -28
View File
@@ -28,62 +28,62 @@ TEST: []u8 = {
0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2b, 0x48,
0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x01, 0x00, 0x15,
0x6a, 0x2c, 0x42, 0x07, 0x00, 0x00, 0x00,
};
}
main :: proc() {
// Set up output buffer.
buf := bytes.Buffer{};
buf := bytes.Buffer{}
stdout :: proc(s: string) {
os.write_string(os.stdout, s);
os.write_string(os.stdout, s)
}
stderr :: proc(s: string) {
os.write_string(os.stderr, s);
os.write_string(os.stderr, s)
}
args := os.args;
args := os.args
if len(args) < 2 {
stderr("No input file specified.\n");
err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST));
stderr("No input file specified.\n")
err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST))
if err == nil {
stdout("Displaying test vector: ");
stdout(bytes.buffer_to_string(&buf));
stdout("\n");
stdout("Displaying test vector: ")
stdout(bytes.buffer_to_string(&buf))
stdout("\n")
} else {
fmt.printf("gzip.load returned %v\n", err);
fmt.printf("gzip.load returned %v\n", err)
}
bytes.buffer_destroy(&buf);
os.exit(0);
bytes.buffer_destroy(&buf)
os.exit(0)
}
// The rest are all files.
args = args[1:];
err: Error;
args = args[1:]
err: Error
for file in args {
if file == "-" {
// Read from stdin
s := os.stream_from_handle(os.stdin);
s := os.stream_from_handle(os.stdin)
ctx := &compress.Context_Stream_Input{
input = s,
};
err = load(ctx, &buf);
}
err = load(ctx, &buf)
} else {
err = load(file, &buf);
err = load(file, &buf)
}
if err != nil {
if err != E_General.File_Not_Found {
stderr("File not found: ");
stderr(file);
stderr("\n");
os.exit(1);
stderr("File not found: ")
stderr(file)
stderr("\n")
os.exit(1)
}
stderr("GZIP returned an error.\n");
bytes.buffer_destroy(&buf);
os.exit(2);
stderr("GZIP returned an error.\n")
bytes.buffer_destroy(&buf)
os.exit(2)
}
stdout(bytes.buffer_to_string(&buf));
stdout(bytes.buffer_to_string(&buf))
}
bytes.buffer_destroy(&buf);
bytes.buffer_destroy(&buf)
}
+98 -98
View File
@@ -33,7 +33,7 @@ Header :: struct #packed {
xfl: Compression_Flags,
os: OS,
}
#assert(size_of(Header) == 10);
#assert(size_of(Header) == 10)
Header_Flag :: enum u8 {
// Order is important
@@ -46,7 +46,7 @@ Header_Flag :: enum u8 {
reserved_2 = 6,
reserved_3 = 7,
}
Header_Flags :: distinct bit_set[Header_Flag; u8];
Header_Flags :: distinct bit_set[Header_Flag; u8]
OS :: enum u8 {
FAT = 0,
@@ -82,7 +82,7 @@ OS_Name :: #partial [OS]string{
.QDOS = "QDOS",
.Acorn_RISCOS = "Acorn RISCOS",
.Unknown = "Unknown",
};
}
Compression :: enum u8 {
DEFLATE = 8,
@@ -93,74 +93,74 @@ Compression_Flags :: enum u8 {
Fastest_Compression = 4,
}
Error :: compress.Error;
E_General :: compress.General_Error;
E_GZIP :: compress.GZIP_Error;
E_ZLIB :: compress.ZLIB_Error;
E_Deflate :: compress.Deflate_Error;
Error :: compress.Error
E_General :: compress.General_Error
E_GZIP :: compress.GZIP_Error
E_ZLIB :: compress.ZLIB_Error
E_Deflate :: compress.Deflate_Error
GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le));
GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le))
load :: proc{load_from_slice, load_from_file, load_from_context};
load :: proc{load_from_slice, load_from_file, load_from_context}
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
data, ok := os.read_entire_file(filename, allocator);
defer delete(data);
data, ok := os.read_entire_file(filename, allocator)
defer delete(data)
err = E_General.File_Not_Found;
err = E_General.File_Not_Found
if ok {
err = load_from_slice(data, buf, len(data), expected_output_size, allocator);
err = load_from_slice(data, buf, len(data), expected_output_size, allocator)
}
return;
return
}
load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
buf := buf;
buf := buf
z := &compress.Context_Memory_Input{
input_data = slice,
output = buf,
};
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator);
}
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator)
}
load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
buf := buf;
expected_output_size := expected_output_size;
buf := buf
expected_output_size := expected_output_size
input_data_consumed := 0;
input_data_consumed := 0
z.output = buf;
z.output = buf
if expected_output_size > GZIP_MAX_PAYLOAD_SIZE {
return E_GZIP.Payload_Size_Exceeds_Max_Payload;
return E_GZIP.Payload_Size_Exceeds_Max_Payload
}
if expected_output_size > compress.COMPRESS_OUTPUT_ALLOCATE_MAX {
return E_GZIP.Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX;
return E_GZIP.Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX
}
b: []u8;
b: []u8
header, e := compress.read_data(z, Header);
header, e := compress.read_data(z, Header)
if e != .None {
return E_General.File_Too_Short;
return E_General.File_Too_Short
}
input_data_consumed += size_of(Header);
input_data_consumed += size_of(Header)
if header.magic != .GZIP {
return E_GZIP.Invalid_GZIP_Signature;
return E_GZIP.Invalid_GZIP_Signature
}
if header.compression_method != .DEFLATE {
return E_General.Unknown_Compression_Method;
return E_General.Unknown_Compression_Method
}
if header.os >= ._Unknown {
header.os = .Unknown;
header.os = .Unknown
}
if .reserved_1 in header.flags || .reserved_2 in header.flags || .reserved_3 in header.flags {
return E_GZIP.Reserved_Flag_Set;
return E_GZIP.Reserved_Flag_Set
}
// printf("signature: %v\n", header.magic);
@@ -171,84 +171,84 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
// printf("os: %v\n", OS_Name[header.os]);
if .extra in header.flags {
xlen, e_extra := compress.read_data(z, u16le);
input_data_consumed += 2;
xlen, e_extra := compress.read_data(z, u16le)
input_data_consumed += 2
if e_extra != .None {
return E_General.Stream_Too_Short;
return E_General.Stream_Too_Short
}
// printf("Extra data present (%v bytes)\n", xlen);
if xlen < 4 {
// Minimum length is 2 for ID + 2 for a field length, if set to zero.
return E_GZIP.Invalid_Extra_Data;
return E_GZIP.Invalid_Extra_Data
}
field_id: [2]u8;
field_length: u16le;
field_error: io.Error;
field_id: [2]u8
field_length: u16le
field_error: io.Error
for xlen >= 4 {
// println("Parsing Extra field(s).");
field_id, field_error = compress.read_data(z, [2]u8);
field_id, field_error = compress.read_data(z, [2]u8)
if field_error != .None {
// printf("Parsing Extra returned: %v\n", field_error);
return E_General.Stream_Too_Short;
return E_General.Stream_Too_Short
}
xlen -= 2;
input_data_consumed += 2;
xlen -= 2
input_data_consumed += 2
field_length, field_error = compress.read_data(z, u16le);
field_length, field_error = compress.read_data(z, u16le)
if field_error != .None {
// printf("Parsing Extra returned: %v\n", field_error);
return E_General.Stream_Too_Short;
return E_General.Stream_Too_Short
}
xlen -= 2;
input_data_consumed += 2;
xlen -= 2
input_data_consumed += 2
if xlen <= 0 {
// We're not going to try and recover by scanning for a ZLIB header.
// Who knows what else is wrong with this file.
return E_GZIP.Invalid_Extra_Data;
return E_GZIP.Invalid_Extra_Data
}
// printf(" Field \"%v\" of length %v found: ", string(field_id[:]), field_length);
if field_length > 0 {
b, field_error = compress.read_slice(z, int(field_length));
b, field_error = compress.read_slice(z, int(field_length))
if field_error != .None {
// printf("Parsing Extra returned: %v\n", field_error);
return E_General.Stream_Too_Short;
return E_General.Stream_Too_Short
}
xlen -= field_length;
input_data_consumed += int(field_length);
xlen -= field_length
input_data_consumed += int(field_length)
// printf("%v\n", string(field_data));
}
if xlen != 0 {
return E_GZIP.Invalid_Extra_Data;
return E_GZIP.Invalid_Extra_Data
}
}
}
if .name in header.flags {
// Should be enough.
name: [1024]u8;
i := 0;
name_error: io.Error;
name: [1024]u8
i := 0
name_error: io.Error
for i < len(name) {
b, name_error = compress.read_slice(z, 1);
b, name_error = compress.read_slice(z, 1)
if name_error != .None {
return E_General.Stream_Too_Short;
return E_General.Stream_Too_Short
}
input_data_consumed += 1;
input_data_consumed += 1
if b[0] == 0 {
break;
break
}
name[i] = b[0];
i += 1;
name[i] = b[0]
i += 1
if i >= len(name) {
return E_GZIP.Original_Name_Too_Long;
return E_GZIP.Original_Name_Too_Long
}
}
// printf("Original filename: %v\n", string(name[:i]));
@@ -256,34 +256,34 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
if .comment in header.flags {
// Should be enough.
comment: [1024]u8;
i := 0;
comment_error: io.Error;
comment: [1024]u8
i := 0
comment_error: io.Error
for i < len(comment) {
b, comment_error = compress.read_slice(z, 1);
b, comment_error = compress.read_slice(z, 1)
if comment_error != .None {
return E_General.Stream_Too_Short;
return E_General.Stream_Too_Short
}
input_data_consumed += 1;
input_data_consumed += 1
if b[0] == 0 {
break;
break
}
comment[i] = b[0];
i += 1;
comment[i] = b[0]
i += 1
if i >= len(comment) {
return E_GZIP.Comment_Too_Long;
return E_GZIP.Comment_Too_Long
}
}
// printf("Comment: %v\n", string(comment[:i]));
}
if .header_crc in header.flags {
crc_error: io.Error;
_, crc_error = compress.read_slice(z, 2);
input_data_consumed += 2;
crc_error: io.Error
_, crc_error = compress.read_slice(z, 2)
input_data_consumed += 2
if crc_error != .None {
return E_General.Stream_Too_Short;
return E_General.Stream_Too_Short
}
/*
We don't actually check the CRC16 (lower 2 bytes of CRC32 of header data until the CRC field).
@@ -294,7 +294,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
/*
We should have arrived at the ZLIB payload.
*/
payload_u32le: u32le;
payload_u32le: u32le
// fmt.printf("known_gzip_size: %v | expected_output_size: %v\n", known_gzip_size, expected_output_size);
@@ -314,12 +314,12 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
*/
if known_gzip_size > -1 {
offset := i64(known_gzip_size - input_data_consumed - 4);
size, _ := compress.input_size(z);
offset := i64(known_gzip_size - input_data_consumed - 4)
size, _ := compress.input_size(z)
if size >= offset + 4 {
length_bytes := z.input_data[offset:][:4];
payload_u32le = (^u32le)(&length_bytes[0])^;
expected_output_size = int(payload_u32le);
length_bytes := z.input_data[offset:][:4]
payload_u32le = (^u32le)(&length_bytes[0])^
expected_output_size = int(payload_u32le)
}
} else {
/*
@@ -331,37 +331,37 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
// fmt.printf("GZIP: Expected Payload Size: %v\n", expected_output_size);
zlib_error := zlib.inflate_raw(z=z, expected_output_size=expected_output_size);
zlib_error := zlib.inflate_raw(z=z, expected_output_size=expected_output_size)
if zlib_error != nil {
return zlib_error;
return zlib_error
}
/*
Read CRC32 using the ctx bit reader because zlib may leave bytes in there.
*/
compress.discard_to_next_byte_lsb(z);
compress.discard_to_next_byte_lsb(z)
footer_error: io.Error;
footer_error: io.Error
payload_crc_b: [4]u8;
payload_crc_b: [4]u8
for _, i in payload_crc_b {
payload_crc_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z);
payload_crc_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z)
}
payload_crc := transmute(u32le)payload_crc_b;
payload_crc := transmute(u32le)payload_crc_b
payload := bytes.buffer_to_bytes(buf);
crc32 := u32le(hash.crc32(payload));
payload := bytes.buffer_to_bytes(buf)
crc32 := u32le(hash.crc32(payload))
if crc32 != payload_crc {
return E_GZIP.Payload_CRC_Invalid;
return E_GZIP.Payload_CRC_Invalid
}
payload_len_b: [4]u8;
payload_len_b: [4]u8
for _, i in payload_len_b {
payload_len_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z);
payload_len_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z)
}
payload_len := transmute(u32le)payload_len_b;
payload_len := transmute(u32le)payload_len_b
if len(payload) != int(payload_len) {
return E_GZIP.Payload_Length_Invalid;
return E_GZIP.Payload_Length_Invalid
}
return nil;
return nil
}
+9 -9
View File
@@ -34,19 +34,19 @@ main :: proc() {
98, 53, 8, 104, 213, 234, 201, 147, 7, 248, 192, 14, 170, 29, 25,
171, 15, 18, 59, 138, 112, 63, 23, 205, 110, 254, 136, 109, 78, 231,
63, 234, 138, 133, 204,
};
OUTPUT_SIZE :: 438;
}
OUTPUT_SIZE :: 438
buf: bytes.Buffer;
buf: bytes.Buffer
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE);
defer bytes.buffer_destroy(&buf);
err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
defer bytes.buffer_destroy(&buf)
if err != nil {
fmt.printf("\nError: %v\n", err);
fmt.printf("\nError: %v\n", err)
}
s := bytes.buffer_to_string(&buf);
fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s);
assert(len(s) == OUTPUT_SIZE);
s := bytes.buffer_to_string(&buf)
fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s)
assert(len(s) == OUTPUT_SIZE)
}
+213 -213
View File
@@ -47,41 +47,41 @@ Options :: struct {
level: u8,
}
Error :: compress.Error;
E_General :: compress.General_Error;
E_ZLIB :: compress.ZLIB_Error;
E_Deflate :: compress.Deflate_Error;
Error :: compress.Error
E_General :: compress.General_Error
E_ZLIB :: compress.ZLIB_Error
E_Deflate :: compress.Deflate_Error
DEFLATE_MAX_CHUNK_SIZE :: 65535;
DEFLATE_MAX_LITERAL_SIZE :: 65535;
DEFLATE_MAX_DISTANCE :: 32768;
DEFLATE_MAX_LENGTH :: 258;
DEFLATE_MAX_CHUNK_SIZE :: 65535
DEFLATE_MAX_LITERAL_SIZE :: 65535
DEFLATE_MAX_DISTANCE :: 32768
DEFLATE_MAX_LENGTH :: 258
HUFFMAN_MAX_BITS :: 16;
HUFFMAN_FAST_BITS :: 9;
HUFFMAN_FAST_MASK :: ((1 << HUFFMAN_FAST_BITS) - 1);
HUFFMAN_MAX_BITS :: 16
HUFFMAN_FAST_BITS :: 9
HUFFMAN_FAST_MASK :: ((1 << HUFFMAN_FAST_BITS) - 1)
Z_LENGTH_BASE := [31]u16{
3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,
67,83,99,115,131,163,195,227,258,0,0,
};
}
Z_LENGTH_EXTRA := [31]u8{
0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,
};
}
Z_DIST_BASE := [32]u16{
1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0,
};
}
Z_DIST_EXTRA := [32]u8{
0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0,
};
}
Z_LENGTH_DEZIGZAG := []u8{
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
};
}
Z_FIXED_LENGTH := [288]u8{
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
@@ -93,17 +93,17 @@ Z_FIXED_LENGTH := [288]u8{
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,
};
}
Z_FIXED_DIST := [32]u8{
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
};
}
/*
Accelerate all cases in default tables.
*/
ZFAST_BITS :: 9;
ZFAST_MASK :: ((1 << ZFAST_BITS) - 1);
ZFAST_BITS :: 9
ZFAST_MASK :: ((1 << ZFAST_BITS) - 1)
/*
ZLIB-style Huffman encoding.
@@ -116,22 +116,22 @@ Huffman_Table :: struct {
firstsymbol: [16]u16,
size: [288]u8,
value: [288]u16,
};
}
// Implementation starts here
@(optimization_mode="speed")
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
assert(bits <= 16);
assert(bits <= 16)
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
// by reversing all of the bits and masking out the unneeded ones.
r = n;
r = ((r & 0xAAAA) >> 1) | ((r & 0x5555) << 1);
r = ((r & 0xCCCC) >> 2) | ((r & 0x3333) << 2);
r = ((r & 0xF0F0) >> 4) | ((r & 0x0F0F) << 4);
r = ((r & 0xFF00) >> 8) | ((r & 0x00FF) << 8);
r = n
r = ((r & 0xAAAA) >> 1) | ((r & 0x5555) << 1)
r = ((r & 0xCCCC) >> 2) | ((r & 0x3333) << 2)
r = ((r & 0xF0F0) >> 4) | ((r & 0x0F0F) << 4)
r = ((r & 0xFF00) >> 8) | ((r & 0x00FF) << 8)
r >>= (16 - bits);
return;
r >>= (16 - bits)
return
}
@@ -145,16 +145,16 @@ grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
/*
Double until we reach the maximum allowed.
*/
new_size := min(len(buf) << 1, compress.COMPRESS_OUTPUT_ALLOCATE_MAX);
resize(buf, new_size);
new_size := min(len(buf) << 1, compress.COMPRESS_OUTPUT_ALLOCATE_MAX)
resize(buf, new_size)
if len(buf) != new_size {
/*
Resize failed.
*/
return .Resize_Failed;
return .Resize_Failed
}
return nil;
return nil
}
/*
@@ -167,17 +167,17 @@ write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_ch
Resize if needed.
*/
if int(z.bytes_written) + 1 >= len(z.output.buf) {
e := grow_buffer(&z.output.buf);
e := grow_buffer(&z.output.buf)
if e != nil {
return .Short_Write;
return .Short_Write
}
}
#no_bounds_check {
z.output.buf[z.bytes_written] = c;
z.output.buf[z.bytes_written] = c
}
z.bytes_written += 1;
return .None;
z.bytes_written += 1
return .None
}
@(optimization_mode="speed")
@@ -192,20 +192,20 @@ repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_chec
Resize if needed.
*/
if int(z.bytes_written) + int(count) >= len(z.output.buf) {
e := grow_buffer(&z.output.buf);
e := grow_buffer(&z.output.buf)
if e != nil {
return .Short_Write;
return .Short_Write
}
}
#no_bounds_check {
for _ in 0..<count {
z.output.buf[z.bytes_written] = c;
z.bytes_written += 1;
z.output.buf[z.bytes_written] = c
z.bytes_written += 1
}
}
return .None;
return .None
}
@(optimization_mode="speed")
@@ -216,178 +216,178 @@ repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) {
the output stream, just give it _that_ slice.
*/
offset := i64(distance);
offset := i64(distance)
if int(z.bytes_written) + int(count) >= len(z.output.buf) {
e := grow_buffer(&z.output.buf);
e := grow_buffer(&z.output.buf)
if e != nil {
return .Short_Write;
return .Short_Write
}
}
#no_bounds_check {
for _ in 0..<count {
c := z.output.buf[z.bytes_written - offset];
z.output.buf[z.bytes_written] = c;
z.bytes_written += 1;
c := z.output.buf[z.bytes_written - offset]
z.output.buf[z.bytes_written] = c
z.bytes_written += 1
}
}
return .None;
return .None
}
allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_Table, err: Error) {
return new(Huffman_Table, allocator), nil;
return new(Huffman_Table, allocator), nil
}
@(optimization_mode="speed")
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
sizes: [HUFFMAN_MAX_BITS+1]int;
next_code: [HUFFMAN_MAX_BITS]int;
sizes: [HUFFMAN_MAX_BITS+1]int
next_code: [HUFFMAN_MAX_BITS]int
k := int(0);
k := int(0)
mem.zero_slice(sizes[:]);
mem.zero_slice(z.fast[:]);
mem.zero_slice(sizes[:])
mem.zero_slice(z.fast[:])
for v in code_lengths {
sizes[v] += 1;
sizes[v] += 1
}
sizes[0] = 0;
sizes[0] = 0
for i in 1..<(HUFFMAN_MAX_BITS+1) {
if sizes[i] > (1 << uint(i)) {
return E_Deflate.Huffman_Bad_Sizes;
return E_Deflate.Huffman_Bad_Sizes
}
}
code := int(0);
code := int(0)
for i in 1..<HUFFMAN_MAX_BITS {
next_code[i] = code;
z.firstcode[i] = u16(code);
z.firstsymbol[i] = u16(k);
code = code + sizes[i];
next_code[i] = code
z.firstcode[i] = u16(code)
z.firstsymbol[i] = u16(k)
code = code + sizes[i]
if sizes[i] != 0 {
if code - 1 >= (1 << u16(i)) {
return E_Deflate.Huffman_Bad_Code_Lengths;
return E_Deflate.Huffman_Bad_Code_Lengths
}
}
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i));
code <<= 1;
k += int(sizes[i]);
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i))
code <<= 1
k += int(sizes[i])
}
z.maxcode[HUFFMAN_MAX_BITS] = 0x10000; // Sentinel
c: int;
z.maxcode[HUFFMAN_MAX_BITS] = 0x10000 // Sentinel
c: int
for v, ci in code_lengths {
if v != 0 {
c = next_code[v] - int(z.firstcode[v]) + int(z.firstsymbol[v]);
fastv := u16((u16(v) << 9) | u16(ci));
z.size[c] = u8(v);
z.value[c] = u16(ci);
c = next_code[v] - int(z.firstcode[v]) + int(z.firstsymbol[v])
fastv := u16((u16(v) << 9) | u16(ci))
z.size[c] = u8(v)
z.value[c] = u16(ci)
if v <= ZFAST_BITS {
j := z_bit_reverse(u16(next_code[v]), v);
j := z_bit_reverse(u16(next_code[v]), v)
for j < (1 << ZFAST_BITS) {
z.fast[j] = fastv;
j += (1 << v);
z.fast[j] = fastv
j += (1 << v)
}
}
next_code[v] += 1;
next_code[v] += 1
}
}
return nil;
return nil
}
@(optimization_mode="speed")
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
code := u16(compress.peek_bits_lsb(z,16));
code := u16(compress.peek_bits_lsb(z,16))
k := int(z_bit_reverse(code, 16));
s: u8;
k := int(z_bit_reverse(code, 16))
s: u8
#no_bounds_check for s = HUFFMAN_FAST_BITS+1; ; {
if k < t.maxcode[s] {
break;
break
}
s += 1;
s += 1
}
if s >= 16 {
return 0, E_Deflate.Bad_Huffman_Code;
return 0, E_Deflate.Bad_Huffman_Code
}
// code size is s, so:
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s]);
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s])
if b >= size_of(t.size) {
return 0, E_Deflate.Bad_Huffman_Code;
return 0, E_Deflate.Bad_Huffman_Code
}
if t.size[b] != s {
return 0, E_Deflate.Bad_Huffman_Code;
return 0, E_Deflate.Bad_Huffman_Code
}
compress.consume_bits_lsb(z, s);
compress.consume_bits_lsb(z, s)
r = t.value[b];
return r, nil;
r = t.value[b]
return r, nil
}
@(optimization_mode="speed")
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
if z.num_bits < 16 {
if z.num_bits > 63 {
return 0, E_ZLIB.Code_Buffer_Malformed;
return 0, E_ZLIB.Code_Buffer_Malformed
}
compress.refill_lsb(z);
compress.refill_lsb(z)
if z.num_bits > 63 {
return 0, E_General.Stream_Too_Short;
return 0, E_General.Stream_Too_Short
}
}
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK];
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]
if b != 0 {
s := u8(b >> ZFAST_BITS);
compress.consume_bits_lsb(z, s);
return b & 511, nil;
s := u8(b >> ZFAST_BITS)
compress.consume_bits_lsb(z, s)
return b & 511, nil
}
return decode_huffman_slowpath(z, t);
return decode_huffman_slowpath(z, t)
}
@(optimization_mode="speed")
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
#no_bounds_check for {
value, e := decode_huffman(z, z_repeat);
value, e := decode_huffman(z, z_repeat)
if e != nil {
return err;
return err
}
if value < 256 {
e := write_byte(z, u8(value));
e := write_byte(z, u8(value))
if e != .None {
return E_General.Output_Too_Short;
return E_General.Output_Too_Short
}
} else {
if value == 256 {
// End of block
return nil;
return nil
}
value -= 257;
length := Z_LENGTH_BASE[value];
value -= 257
length := Z_LENGTH_BASE[value]
if Z_LENGTH_EXTRA[value] > 0 {
length += u16(compress.read_bits_lsb(z, Z_LENGTH_EXTRA[value]));
length += u16(compress.read_bits_lsb(z, Z_LENGTH_EXTRA[value]))
}
value, e = decode_huffman(z, z_offset);
value, e = decode_huffman(z, z_offset)
if e != nil {
return E_Deflate.Bad_Huffman_Code;
return E_Deflate.Bad_Huffman_Code
}
distance := Z_DIST_BASE[value];
distance := Z_DIST_BASE[value]
if Z_DIST_EXTRA[value] > 0 {
distance += u16(compress.read_bits_lsb(z, Z_DIST_EXTRA[value]));
distance += u16(compress.read_bits_lsb(z, Z_DIST_EXTRA[value]))
}
if z.bytes_written < i64(distance) {
// Distance is longer than we've decoded so far.
return E_Deflate.Bad_Distance;
return E_Deflate.Bad_Distance
}
/*
@@ -402,17 +402,17 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
Replicate the last outputted byte, length times.
*/
if length > 0 {
c := z.output.buf[z.bytes_written - i64(distance)];
e := repl_byte(z, length, c);
c := z.output.buf[z.bytes_written - i64(distance)]
e := repl_byte(z, length, c)
if e != .None {
return E_General.Output_Too_Short;
return E_General.Output_Too_Short
}
}
} else {
if length > 0 {
e := repl_bytes(z, length, distance);
e := repl_bytes(z, length, distance)
if e != .None {
return E_General.Output_Too_Short;
return E_General.Output_Too_Short
}
}
}
@@ -430,27 +430,27 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
*/
if !raw {
size, size_err := compress.input_size(ctx);
size, size_err := compress.input_size(ctx)
if size < 6 || size_err != nil {
return E_General.Stream_Too_Short;
return E_General.Stream_Too_Short
}
cmf, _ := compress.read_u8(ctx);
cmf, _ := compress.read_u8(ctx)
method := Compression_Method(cmf & 0xf);
method := Compression_Method(cmf & 0xf)
if method != .DEFLATE {
return E_General.Unknown_Compression_Method;
return E_General.Unknown_Compression_Method
}
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
return E_ZLIB.Unsupported_Window_Size;
return E_ZLIB.Unsupported_Window_Size
}
flg, _ := compress.read_u8(ctx);
flg, _ := compress.read_u8(ctx)
fcheck := flg & 0x1f;
fcheck_computed := (cmf << 8 | flg) & 0x1f;
fcheck := flg & 0x1f
fcheck_computed := (cmf << 8 | flg) & 0x1f
if fcheck != fcheck_computed {
return E_General.Checksum_Failed;
return E_General.Checksum_Failed
}
/*
@@ -458,7 +458,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
They're application specific and PNG doesn't use them.
*/
if fdict := (flg >> 5) & 1; fdict != 0 {
return E_ZLIB.FDICT_Unsupported;
return E_ZLIB.FDICT_Unsupported
}
// flevel := Compression_Level((flg >> 6) & 3);
@@ -471,36 +471,36 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
}
// Parse ZLIB stream without header.
inflate_raw(z=ctx, expected_output_size=expected_output_size) or_return;
inflate_raw(z=ctx, expected_output_size=expected_output_size) or_return
if !raw {
compress.discard_to_next_byte_lsb(ctx);
compress.discard_to_next_byte_lsb(ctx)
adler_b: [4]u8;
adler_b: [4]u8
for _, i in adler_b {
adler_b[i], _ = compress.read_u8_prefer_code_buffer_lsb(ctx);
adler_b[i], _ = compress.read_u8_prefer_code_buffer_lsb(ctx)
}
adler := transmute(u32be)adler_b;
adler := transmute(u32be)adler_b
output_hash := hash.adler32(ctx.output.buf[:]);
output_hash := hash.adler32(ctx.output.buf[:])
if output_hash != u32(adler) {
return E_General.Checksum_Failed;
return E_General.Checksum_Failed
}
}
return nil;
return nil
}
// TODO: Check alignment of reserve/resize.
@(optimization_mode="speed")
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
expected_output_size := expected_output_size;
expected_output_size := expected_output_size
/*
Always set up a minimum allocation size.
*/
expected_output_size = max(max(expected_output_size, compress.COMPRESS_OUTPUT_ALLOCATE_MIN), 512);
expected_output_size = max(max(expected_output_size, compress.COMPRESS_OUTPUT_ALLOCATE_MIN), 512)
// fmt.printf("\nZLIB: Expected Payload Size: %v\n\n", expected_output_size);
@@ -508,34 +508,34 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
/*
Try to pre-allocate the output buffer.
*/
reserve(&z.output.buf, expected_output_size);
resize (&z.output.buf, expected_output_size);
};
if len(z.output.buf) != expected_output_size {
return .Resize_Failed;
reserve(&z.output.buf, expected_output_size)
resize (&z.output.buf, expected_output_size)
}
z.num_bits = 0;
z.code_buffer = 0;
if len(z.output.buf) != expected_output_size {
return .Resize_Failed
}
z_repeat: ^Huffman_Table;
z_offset: ^Huffman_Table;
codelength_ht: ^Huffman_Table;
defer free(z_repeat);
defer free(z_offset);
defer free(codelength_ht);
z.num_bits = 0
z.code_buffer = 0
z_repeat = allocate_huffman_table(allocator=context.allocator) or_return;
z_offset = allocate_huffman_table(allocator=context.allocator) or_return;
codelength_ht = allocate_huffman_table(allocator=context.allocator) or_return;
z_repeat: ^Huffman_Table
z_offset: ^Huffman_Table
codelength_ht: ^Huffman_Table
defer free(z_repeat)
defer free(z_offset)
defer free(codelength_ht)
final := u32(0);
type := u32(0);
z_repeat = allocate_huffman_table(allocator=context.allocator) or_return
z_offset = allocate_huffman_table(allocator=context.allocator) or_return
codelength_ht = allocate_huffman_table(allocator=context.allocator) or_return
final := u32(0)
type := u32(0)
for {
final = compress.read_bits_lsb(z, 1);
type = compress.read_bits_lsb(z, 2);
final = compress.read_bits_lsb(z, 1)
type = compress.read_bits_lsb(z, 2)
// fmt.printf("Final: %v | Type: %v\n", final, type);
@@ -544,16 +544,16 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
// Uncompressed block
// Discard bits until next byte boundary
compress.discard_to_next_byte_lsb(z);
compress.discard_to_next_byte_lsb(z)
uncompressed_len := i16(compress.read_bits_lsb(z, 16));
length_check := i16(compress.read_bits_lsb(z, 16));
uncompressed_len := i16(compress.read_bits_lsb(z, 16))
length_check := i16(compress.read_bits_lsb(z, 16))
// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check);
if ~uncompressed_len != length_check {
return E_Deflate.Len_Nlen_Mismatch;
return E_Deflate.Len_Nlen_Mismatch
}
/*
@@ -561,116 +561,116 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
and a single Adler32 update after.
*/
#no_bounds_check for uncompressed_len > 0 {
compress.refill_lsb(z);
lit := compress.read_bits_lsb(z, 8);
write_byte(z, u8(lit));
uncompressed_len -= 1;
compress.refill_lsb(z)
lit := compress.read_bits_lsb(z, 8)
write_byte(z, u8(lit))
uncompressed_len -= 1
}
case 3:
return E_Deflate.BType_3;
return E_Deflate.BType_3
case:
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
if type == 1 {
// Use fixed code lengths.
build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return;
build_huffman(z_offset, Z_FIXED_DIST[:]) or_return;
build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return
build_huffman(z_offset, Z_FIXED_DIST[:]) or_return
} else {
lencodes: [286+32+137]u8;
codelength_sizes: [19]u8;
lencodes: [286+32+137]u8
codelength_sizes: [19]u8
//i: u32;
n: u32;
n: u32
compress.refill_lsb(z, 14);
hlit := compress.read_bits_no_refill_lsb(z, 5) + 257;
hdist := compress.read_bits_no_refill_lsb(z, 5) + 1;
hclen := compress.read_bits_no_refill_lsb(z, 4) + 4;
ntot := hlit + hdist;
compress.refill_lsb(z, 14)
hlit := compress.read_bits_no_refill_lsb(z, 5) + 257
hdist := compress.read_bits_no_refill_lsb(z, 5) + 1
hclen := compress.read_bits_no_refill_lsb(z, 4) + 4
ntot := hlit + hdist
#no_bounds_check for i in 0..<hclen {
s := compress.read_bits_lsb(z, 3);
codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s);
s := compress.read_bits_lsb(z, 3)
codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s)
}
build_huffman(codelength_ht, codelength_sizes[:]) or_return;
build_huffman(codelength_ht, codelength_sizes[:]) or_return
n = 0;
c: u16;
n = 0
c: u16
for n < ntot {
c = decode_huffman(z, codelength_ht) or_return;
c = decode_huffman(z, codelength_ht) or_return
if c < 0 || c >= 19 {
return E_Deflate.Huffman_Bad_Code_Lengths;
return E_Deflate.Huffman_Bad_Code_Lengths
}
if c < 16 {
lencodes[n] = u8(c);
n += 1;
lencodes[n] = u8(c)
n += 1
} else {
fill := u8(0);
compress.refill_lsb(z, 7);
fill := u8(0)
compress.refill_lsb(z, 7)
switch c {
case 16:
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3);
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3)
if n == 0 {
return E_Deflate.Huffman_Bad_Code_Lengths;
return E_Deflate.Huffman_Bad_Code_Lengths
}
fill = lencodes[n - 1];
fill = lencodes[n - 1]
case 17:
c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3);
c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3)
case 18:
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11);
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11)
case:
return E_Deflate.Huffman_Bad_Code_Lengths;
return E_Deflate.Huffman_Bad_Code_Lengths
}
if ntot - n < u32(c) {
return E_Deflate.Huffman_Bad_Code_Lengths;
return E_Deflate.Huffman_Bad_Code_Lengths
}
nc := n + u32(c);
nc := n + u32(c)
#no_bounds_check for ; n < nc; n += 1 {
lencodes[n] = fill;
lencodes[n] = fill
}
}
}
if n != ntot {
return E_Deflate.Huffman_Bad_Code_Lengths;
return E_Deflate.Huffman_Bad_Code_Lengths
}
build_huffman(z_repeat, lencodes[:hlit]) or_return;
build_huffman(z_offset, lencodes[hlit:ntot]) or_return;
build_huffman(z_repeat, lencodes[:hlit]) or_return
build_huffman(z_offset, lencodes[hlit:ntot]) or_return
}
parse_huffman_block(z, z_repeat, z_offset) or_return;
parse_huffman_block(z, z_repeat, z_offset) or_return
}
if final == 1 {
break;
break
}
}
if int(z.bytes_written) != len(z.output.buf) {
resize(&z.output.buf, int(z.bytes_written));
resize(&z.output.buf, int(z.bytes_written))
}
return nil;
return nil
}
inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
ctx := compress.Context_Memory_Input{};
ctx := compress.Context_Memory_Input{}
ctx.input_data = input;
ctx.output = buf;
ctx.input_data = input
ctx.output = buf
return inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size);
return inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size)
}
inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
ctx := compress.Context_Memory_Input{};
ctx := compress.Context_Memory_Input{}
ctx.input_data = input;
ctx.output = buf;
ctx.input_data = input
ctx.output = buf
return inflate_raw(z=&ctx, expected_output_size=expected_output_size);
return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
}
inflate :: proc{inflate_from_context, inflate_from_byte_array};
+69 -69
View File
@@ -10,7 +10,7 @@ Array :: struct($T: typeid) {
allocator: mem.Allocator,
}
ARRAY_DEFAULT_CAPACITY :: 16;
ARRAY_DEFAULT_CAPACITY :: 16
/*
array_init :: proc {
@@ -46,171 +46,171 @@ array_grow
array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) {
array_init_len_cap(a, 0, ARRAY_DEFAULT_CAPACITY, allocator);
array_init_len_cap(a, 0, ARRAY_DEFAULT_CAPACITY, allocator)
}
array_init_len :: proc(a: ^$A/Array, len: int, allocator := context.allocator) {
array_init_len_cap(a, len, len, allocator);
array_init_len_cap(a, len, len, allocator)
}
array_init_len_cap :: proc(a: ^$A/Array($T), len: int, cap: int, allocator := context.allocator) {
a.allocator = allocator;
a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), a.allocator));
a.len = len;
a.cap = cap;
a.allocator = allocator
a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), a.allocator))
a.len = len
a.cap = cap
}
array_init :: proc{array_init_none, array_init_len, array_init_len_cap};
array_init :: proc{array_init_none, array_init_len, array_init_len_cap}
array_delete :: proc(a: $A/Array) {
mem.free(a.data, a.allocator);
mem.free(a.data, a.allocator)
}
array_len :: proc(a: $A/Array) -> int {
return a.len;
return a.len
}
array_cap :: proc(a: $A/Array) -> int {
return a.cap;
return a.cap
}
array_space :: proc(a: $A/Array) -> int {
return a.cap - a.len;
return a.cap - a.len
}
array_slice :: proc(a: $A/Array($T)) -> []T {
s := mem.Raw_Slice{a.data, a.len};
return transmute([]T)s;
s := mem.Raw_Slice{a.data, a.len}
return transmute([]T)s
}
array_cap_slice :: proc(a: $A/Array($T)) -> []T {
s := mem.Raw_Slice{a.data, a.cap};
return transmute([]T)s;
s := mem.Raw_Slice{a.data, a.cap}
return transmute([]T)s
}
array_get :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> T {
runtime.bounds_check_error_loc(loc, index, array_len(a));
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^;
runtime.bounds_check_error_loc(loc, index, array_len(a))
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^
}
array_get_ptr :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> ^T {
runtime.bounds_check_error_loc(loc, index, array_len(a));
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index));
runtime.bounds_check_error_loc(loc, index, array_len(a))
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))
}
array_set :: proc(a: ^$A/Array($T), index: int, item: T, loc := #caller_location) {
runtime.bounds_check_error_loc(loc, index, array_len(a^));
(^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item;
runtime.bounds_check_error_loc(loc, index, array_len(a^))
(^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item
}
array_reserve :: proc(a: ^$A/Array, capacity: int) {
if capacity > a.len {
array_set_capacity(a, capacity);
array_set_capacity(a, capacity)
}
}
array_resize :: proc(a: ^$A/Array, length: int) {
if length > a.len {
array_set_capacity(a, length);
array_set_capacity(a, length)
}
a.len = length;
a.len = length
}
array_push_back :: proc(a: ^$A/Array($T), item: T) {
if array_space(a^) == 0 {
array_grow(a);
array_grow(a)
}
a.len += 1;
array_set(a, a.len-1, item);
a.len += 1
array_set(a, a.len-1, item)
}
array_push_front :: proc(a: ^$A/Array($T), item: T) {
if array_space(a^) == 0 {
array_grow(a);
array_grow(a)
}
a.len += 1;
data := array_slice(a^);
copy(data[1:], data[:]);
data[0] = item;
a.len += 1
data := array_slice(a^)
copy(data[1:], data[:])
data[0] = item
}
array_pop_back :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
assert(condition=a.len > 0, loc=loc);
item := array_get(a^, a.len-1);
a.len -= 1;
return item;
assert(condition=a.len > 0, loc=loc)
item := array_get(a^, a.len-1)
a.len -= 1
return item
}
array_pop_front :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
assert(condition=a.len > 0, loc=loc);
item := array_get(a^, 0);
s := array_slice(a^);
copy(s[:], s[1:]);
a.len -= 1;
return item;
assert(condition=a.len > 0, loc=loc)
item := array_get(a^, 0)
s := array_slice(a^)
copy(s[:], s[1:])
a.len -= 1
return item
}
array_consume :: proc(a: ^$A/Array($T), count: int, loc := #caller_location) {
assert(condition=a.len >= count, loc=loc);
a.len -= count;
assert(condition=a.len >= count, loc=loc)
a.len -= count
}
array_trim :: proc(a: ^$A/Array($T)) {
array_set_capacity(a, a.len);
array_set_capacity(a, a.len)
}
array_clear :: proc(a: ^$A/Array($T)) {
array_resize(a, 0);
array_resize(a, 0)
}
array_clone :: proc(a: $A/Array($T), allocator := context.allocator) -> A {
res: A;
array_init(&res, array_len(a), array_len(a), allocator);
copy(array_slice(res), array_slice(a));
return res;
res: A
array_init(&res, array_len(a), array_len(a), allocator)
copy(array_slice(res), array_slice(a))
return res
}
array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) {
if array_space(a^) < len(items) {
array_grow(a, a.len + len(items));
array_grow(a, a.len + len(items))
}
offset := a.len;
data := array_cap_slice(a^);
n := copy(data[a.len:], items);
a.len += n;
offset := a.len
data := array_cap_slice(a^)
n := copy(data[a.len:], items)
a.len += n
}
array_push :: proc{array_push_back, array_push_back_elems};
array_append :: proc{array_push_back, array_push_back_elems};
array_push :: proc{array_push_back, array_push_back_elems}
array_append :: proc{array_push_back, array_push_back_elems}
array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) {
if new_capacity == a.cap {
return;
return
}
if new_capacity < a.len {
array_resize(a, new_capacity);
array_resize(a, new_capacity)
}
new_data: ^T;
new_data: ^T
if new_capacity > 0 {
if a.allocator.procedure == nil {
a.allocator = context.allocator;
a.allocator = context.allocator
}
new_data = (^T)(mem.alloc(size_of(T)*new_capacity, align_of(T), a.allocator));
new_data = (^T)(mem.alloc(size_of(T)*new_capacity, align_of(T), a.allocator))
if new_data != nil {
mem.copy(new_data, a.data, size_of(T)*a.len);
mem.copy(new_data, a.data, size_of(T)*a.len)
}
}
mem.free(a.data, a.allocator);
a.data = new_data;
a.cap = new_capacity;
mem.free(a.data, a.allocator)
a.data = new_data
a.cap = new_capacity
}
array_grow :: proc(a: ^$A/Array, min_capacity: int = 0) {
new_capacity := max(array_len(a^)*2 + 8, min_capacity);
array_set_capacity(a, new_capacity);
new_capacity := max(array_len(a^)*2 + 8, min_capacity)
array_set_capacity(a, new_capacity)
}
+27 -27
View File
@@ -2,7 +2,7 @@ package container
import "core:mem"
Bloom_Hash_Proc :: #type proc(data: []byte) -> u32;
Bloom_Hash_Proc :: #type proc(data: []byte) -> u32
Bloom_Hash :: struct {
hash_proc: Bloom_Hash_Proc,
@@ -16,65 +16,65 @@ Bloom_Filter :: struct {
}
bloom_filter_init :: proc(b: ^Bloom_Filter, size: int, allocator := context.allocator) {
b.allocator = allocator;
b.bits = make([]byte, size, allocator);
b.allocator = allocator
b.bits = make([]byte, size, allocator)
}
bloom_filter_destroy :: proc(b: ^Bloom_Filter) {
context.allocator = b.allocator;
delete(b.bits);
context.allocator = b.allocator
delete(b.bits)
for b.hash != nil {
hash := b.hash;
b.hash = b.hash.next;
free(hash);
hash := b.hash
b.hash = b.hash.next
free(hash)
}
}
bloom_filter_add_hash_proc :: proc(b: ^Bloom_Filter, hash_proc: Bloom_Hash_Proc) {
context.allocator = b.allocator;
h := new(Bloom_Hash);
h.hash_proc = hash_proc;
context.allocator = b.allocator
h := new(Bloom_Hash)
h.hash_proc = hash_proc
head := &b.hash;
head := &b.hash
for head^ != nil {
head = &(head^.next);
head = &(head^.next)
}
head^ = h;
head^ = h
}
bloom_filter_add :: proc(b: ^Bloom_Filter, item: []byte) {
#no_bounds_check for h := b.hash; h != nil; h = h.next {
hash := h.hash_proc(item);
hash %= u32(len(b.bits) * 8);
b.bits[hash >> 3] |= 1 << (hash & 3);
hash := h.hash_proc(item)
hash %= u32(len(b.bits) * 8)
b.bits[hash >> 3] |= 1 << (hash & 3)
}
}
bloom_filter_add_string :: proc(b: ^Bloom_Filter, item: string) {
bloom_filter_add(b, transmute([]byte)item);
bloom_filter_add(b, transmute([]byte)item)
}
bloom_filter_add_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) {
item := mem.slice_ptr((^byte)(data), size);
bloom_filter_add(b, item);
item := mem.slice_ptr((^byte)(data), size)
bloom_filter_add(b, item)
}
bloom_filter_test :: proc(b: ^Bloom_Filter, item: []byte) -> bool {
#no_bounds_check for h := b.hash; h != nil; h = h.next {
hash := h.hash_proc(item);
hash %= u32(len(b.bits) * 8);
hash := h.hash_proc(item)
hash %= u32(len(b.bits) * 8)
if (b.bits[hash >> 3] & (1 << (hash & 3)) == 0) {
return false;
return false
}
}
return true;
return true
}
bloom_filter_test_string :: proc(b: ^Bloom_Filter, item: string) -> bool {
return bloom_filter_test(b, transmute([]byte)item);
return bloom_filter_test(b, transmute([]byte)item)
}
bloom_filter_test_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) -> bool {
item := mem.slice_ptr((^byte)(data), size);
return bloom_filter_test(b, item);
item := mem.slice_ptr((^byte)(data), size)
return bloom_filter_test(b, item)
}
+131 -131
View File
@@ -1,7 +1,7 @@
package container
import "core:intrinsics"
_ :: intrinsics;
_ :: intrinsics
Map :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
@@ -49,187 +49,187 @@ multi_map_remove_all
*/
map_init :: proc{map_init_none, map_init_cap};
map_init :: proc{map_init_none, map_init_cap}
map_init_none :: proc(m: ^$M/Map($Key, $Value), allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
m.hash.allocator = allocator
m.entries.allocator = allocator
}
map_init_cap :: proc(m: ^$M/Map($Key, $Value), cap: int, allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
map_reserve(m, cap);
m.hash.allocator = allocator
m.entries.allocator = allocator
map_reserve(m, cap)
}
map_delete :: proc(m: $M/Map($Key, $Value)) {
array_delete(m.hash);
array_delete(m.entries);
array_delete(m.hash)
array_delete(m.entries)
}
map_has :: proc(m: $M/Map($Key, $Value), key: Key) -> bool {
return _map_find_or_fail(m, key) >= 0;
return _map_find_or_fail(m, key) >= 0
}
map_get :: proc(m: $M/Map($Key, $Value), key: Key) -> (res: Value, ok: bool) #optional_ok {
i := _map_find_or_fail(m, key);
i := _map_find_or_fail(m, key)
if i < 0 {
return {}, false;
return {}, false
}
return array_get(m.entries, i).value, true;
return array_get(m.entries, i).value, true
}
map_get_default :: proc(m: $M/Map($Key, $Value), key: Key, default: Value) -> (res: Value, ok: bool) #optional_ok {
i := _map_find_or_fail(m, key);
i := _map_find_or_fail(m, key)
if i < 0 {
return default, false;
return default, false
}
return array_get(m.entries, i).value, true;
return array_get(m.entries, i).value, true
}
map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value {
i := _map_find_or_fail(m, key);
i := _map_find_or_fail(m, key)
if i < 0 {
return nil;
return nil
}
return array_get_ptr(m.entries, i).value;
return array_get_ptr(m.entries, i).value
}
map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
if array_len(m.hash) == 0 {
_map_grow(m);
_map_grow(m)
}
i := _map_find_or_make(m, key);
array_get_ptr(m.entries, i).value = value;
i := _map_find_or_make(m, key)
array_get_ptr(m.entries, i).value = value
if _map_full(m^) {
_map_grow(m);
_map_grow(m)
}
}
map_remove :: proc(m: ^$M/Map($Key, $Value), key: Key) {
fr := _map_find_key(m^, key);
fr := _map_find_key(m^, key)
if fr.entry_index >= 0 {
_map_erase(m, fr);
_map_erase(m, fr)
}
}
map_reserve :: proc(m: ^$M/Map($Key, $Value), new_size: int) {
nm: M;
map_init(&nm, m.hash.allocator);
array_resize(&nm.hash, new_size);
array_reserve(&nm.entries, array_len(m.entries));
nm: M
map_init(&nm, m.hash.allocator)
array_resize(&nm.hash, new_size)
array_reserve(&nm.entries, array_len(m.entries))
for i in 0..<new_size {
array_set(&nm.hash, i, -1);
array_set(&nm.hash, i, -1)
}
for i in 0..<array_len(m.entries) {
e := array_get(m.entries, i);
multi_map_insert(&nm, e.key, e.value);
e := array_get(m.entries, i)
multi_map_insert(&nm, e.key, e.value)
}
map_delete(m^);
m^ = nm;
map_delete(m^)
m^ = nm
}
map_clear :: proc(m: ^$M/Map($Key, $Value)) {
array_clear(&m.hash);
array_clear(&m.entries);
array_clear(&m.hash)
array_clear(&m.entries)
}
multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Key, Value) {
i := _map_find_or_fail(m, key);
i := _map_find_or_fail(m, key)
if i < 0 {
return nil;
return nil
}
return array_get_ptr(m.entries, i);
return array_get_ptr(m.entries, i)
}
multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> ^Map_Entry(Key, Value) {
i := e.next;
i := e.next
for i >= 0 {
it := array_get_ptr(m.entries, i);
it := array_get_ptr(m.entries, i)
if it.hash == e.hash && it.key == e.key {
return it;
return it
}
i = it.next;
i = it.next
}
return nil;
return nil
}
multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
n := 0;
e := multi_map_find_first(m, key);
n := 0
e := multi_map_find_first(m, key)
for e != nil {
n += 1;
e = multi_map_find_next(m, e);
n += 1
e = multi_map_find_next(m, e)
}
return n;
return n
}
multi_map_get :: proc{multi_map_get_array, multi_map_get_slice};
multi_map_get :: proc{multi_map_get_array, multi_map_get_slice}
multi_map_get_array :: proc(m: $M/Map($Key, $Value), key: Key, items: ^Array(Value)) {
if items == nil {
return;
return
}
e := multi_map_find_first(m, key);
e := multi_map_find_first(m, key)
for e != nil {
array_append(items, e.value);
e = multi_map_find_next(m, e);
array_append(items, e.value)
e = multi_map_find_next(m, e)
}
}
multi_map_get_slice :: proc(m: $M/Map($Key, $Value), key: Key, items: []Value) {
e := multi_map_find_first(m, key);
i := 0;
e := multi_map_find_first(m, key)
i := 0
for e != nil && i < len(items) {
items[i] = e.value;
i += 1;
e = multi_map_find_next(m, e);
items[i] = e.value
i += 1
e = multi_map_find_next(m, e)
}
}
multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value {
items: Array(Value);
array_init(&items, 0);
items: Array(Value)
array_init(&items, 0)
e := multi_map_find_first(m, key);
e := multi_map_find_first(m, key)
for e != nil {
array_append(&items, e.value);
e = multi_map_find_next(m, e);
array_append(&items, e.value)
e = multi_map_find_next(m, e)
}
return array_slice(items);
return array_slice(items)
}
multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
if array_len(m.hash) == 0 {
_map_grow(m);
_map_grow(m)
}
i := _map_make(m, key);
array_get_ptr(m.entries, i).value = value;
i := _map_make(m, key)
array_get_ptr(m.entries, i).value = value
if _map_full(m^) {
_map_grow(m);
_map_grow(m)
}
}
multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) {
fr := _map_find_entry(m, e);
fr := _map_find_entry(m, e)
if fr.entry_index >= 0 {
_map_erase(m, fr);
_map_erase(m, fr)
}
}
multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) {
for map_exist(m^, key) {
map_remove(m, key);
map_remove(m, key)
}
}
@@ -244,134 +244,134 @@ Map_Find_Result :: struct {
}
_map_add_entry :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int where intrinsics.type_is_valid_map_key(Key) {
hasher := intrinsics.type_hasher_proc(Key);
hasher := intrinsics.type_hasher_proc(Key)
e: Map_Entry(Key, Value);
e.key = key;
e.hash = hasher(&e.key, 0);
e.next = -1;
idx := array_len(m.entries);
array_push(&m.entries, e);
return idx;
e: Map_Entry(Key, Value)
e.key = key
e.hash = hasher(&e.key, 0)
e.next = -1
idx := array_len(m.entries)
array_push(&m.entries, e)
return idx
}
_map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next);
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next)
} else {
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next;
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next
}
if fr.entry_index == array_len(m.entries)-1 {
array_pop_back(&m.entries);
return;
array_pop_back(&m.entries)
return
}
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1));
last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key);
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1))
last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key)
if last.entry_prev < 0 {
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index;
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index
} else {
array_set(&m.hash, last.hash_index, fr.entry_index);
array_set(&m.hash, last.hash_index, fr.entry_index)
}
}
_map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result where intrinsics.type_is_valid_map_key(Key) {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
fr.entry_index = -1;
fr: Map_Find_Result
fr.hash_index = -1
fr.entry_prev = -1
fr.entry_index = -1
if array_len(m.hash) == 0 {
return fr;
return fr
}
hasher := intrinsics.type_hasher_proc(Key);
hasher := intrinsics.type_hasher_proc(Key)
key := key;
hash := hasher(&key, 0);
key := key
hash := hasher(&key, 0)
fr.hash_index = int(hash % uintptr(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
fr.hash_index = int(hash % uintptr(array_len(m.hash)))
fr.entry_index = array_get(m.hash, fr.hash_index)
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
it := array_get_ptr(m.entries, fr.entry_index)
if it.hash == hash && it.key == key {
return fr;
return fr
}
fr.entry_prev = fr.entry_index;
fr.entry_index = it.next;
fr.entry_prev = fr.entry_index
fr.entry_index = it.next
}
return fr;
return fr
}
_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> Map_Find_Result {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
fr.entry_index = -1;
fr: Map_Find_Result
fr.hash_index = -1
fr.entry_prev = -1
fr.entry_index = -1
if array_len(m.hash) == 0 {
return fr;
return fr
}
fr.hash_index = int(e.hash % uintptr(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
fr.hash_index = int(e.hash % uintptr(array_len(m.hash)))
fr.entry_index = array_get(m.hash, fr.hash_index)
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
it := array_get_ptr(m.entries, fr.entry_index)
if it == e {
return fr;
return fr
}
fr.entry_prev = fr.entry_index;
fr.entry_index = it.next;
fr.entry_prev = fr.entry_index
fr.entry_index = it.next
}
return fr;
return fr
}
_map_find_or_fail :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
return _map_find_key(m, key).entry_index;
return _map_find_key(m, key).entry_index
}
_map_find_or_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
fr := _map_find_key(m^, key);
fr := _map_find_key(m^, key)
if fr.entry_index >= 0 {
return fr.entry_index;
return fr.entry_index
}
i := _map_add_entry(m, key);
i := _map_add_entry(m, key)
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i);
array_set(&m.hash, fr.hash_index, i)
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i;
array_get_ptr(m.entries, fr.entry_prev).next = i
}
return i;
return i
}
_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
fr := _map_find_key(m^, key);
i := _map_add_entry(m, key);
fr := _map_find_key(m^, key)
i := _map_add_entry(m, key)
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i);
array_set(&m.hash, fr.hash_index, i)
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i;
array_get_ptr(m.entries, fr.entry_prev).next = i
}
array_get_ptr(m.entries, i).next = fr.entry_index;
array_get_ptr(m.entries, i).next = fr.entry_index
return i;
return i
}
_map_full :: proc(m: $M/Map($Key, $Value)) -> bool {
// TODO(bill): Determine good max load factor
return array_len(m.entries) >= (array_len(m.hash) / 4)*3;
return array_len(m.entries) >= (array_len(m.hash) / 4)*3
}
_map_grow :: proc(m: ^$M/Map($Key, $Value)) {
new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate
map_reserve(m, new_size);
new_size := array_len(m.entries) * 4 + 7 // TODO(bill): Determine good grow rate
map_reserve(m, new_size)
}
+43 -43
View File
@@ -7,107 +7,107 @@ Priority_Queue :: struct($T: typeid) {
}
priority_queue_init_none :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, allocator := context.allocator) {
queue_init_len(q, f, 0, allocator);
queue_init_len(q, f, 0, allocator)
}
priority_queue_init_len :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, allocator := context.allocator) {
queue_init_len_cap(q, f, 0, 16, allocator);
queue_init_len_cap(q, f, 0, 16, allocator)
}
priority_queue_init_len_cap :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, cap: int, allocator := context.allocator) {
array_init(&q.data, len, cap, allocator);
q.len = len;
q.priority = f;
array_init(&q.data, len, cap, allocator)
q.len = len
q.priority = f
}
priority_queue_init :: proc{priority_queue_init_none, priority_queue_init_len, priority_queue_init_len_cap};
priority_queue_init :: proc{priority_queue_init_none, priority_queue_init_len, priority_queue_init_len_cap}
priority_queue_delete :: proc(q: $Q/Priority_Queue($T)) {
array_delete(q.data);
array_delete(q.data)
}
priority_queue_clear :: proc(q: ^$Q/Priority_Queue($T)) {
q.len = 0;
q.len = 0
}
priority_queue_len :: proc(q: $Q/Priority_Queue($T)) -> int {
return q.len;
return q.len
}
priority_queue_cap :: proc(q: $Q/Priority_Queue($T)) -> int {
return array_cap(q.data);
return array_cap(q.data)
}
priority_queue_space :: proc(q: $Q/Priority_Queue($T)) -> int {
return array_len(q.data) - q.len;
return array_len(q.data) - q.len
}
priority_queue_reserve :: proc(q: ^$Q/Priority_Queue($T), capacity: int) {
if capacity > q.len {
array_resize(&q.data, new_capacity);
array_resize(&q.data, new_capacity)
}
}
priority_queue_resize :: proc(q: ^$Q/Priority_Queue($T), length: int) {
if length > q.len {
array_resize(&q.data, new_capacity);
array_resize(&q.data, new_capacity)
}
q.len = length;
q.len = length
}
_priority_queue_grow :: proc(q: ^$Q/Priority_Queue($T), min_capacity: int = 0) {
new_capacity := max(array_len(q.data)*2 + 8, min_capacity);
array_resize(&q.data, new_capacity);
new_capacity := max(array_len(q.data)*2 + 8, min_capacity)
array_resize(&q.data, new_capacity)
}
priority_queue_push :: proc(q: ^$Q/Priority_Queue($T), item: T) {
if array_len(q.data) - q.len == 0 {
_priority_queue_grow(q);
_priority_queue_grow(q)
}
s := array_slice(q.data);
s[q.len] = item;
s := array_slice(q.data)
s[q.len] = item
i := q.len;
i := q.len
for i > 0 {
p := (i - 1) / 2;
if q.priority(s[p]) <= q.priority(item) do break;
s[i] = s[p];
i = p;
p := (i - 1) / 2
if q.priority(s[p]) <= q.priority(item) do break
s[i] = s[p]
i = p
}
q.len += 1;
if q.len > 0 do s[i] = item;
q.len += 1
if q.len > 0 do s[i] = item
}
priority_queue_pop :: proc(q: ^$Q/Priority_Queue($T)) -> T {
assert(q.len > 0);
assert(q.len > 0)
s := array_slice(q.data);
min := s[0];
root := s[q.len-1];
q.len -= 1;
s := array_slice(q.data)
min := s[0]
root := s[q.len-1]
q.len -= 1
i := 0;
i := 0
for i * 2 + 1 < q.len {
a := i * 2 + 1;
b := i * 2 + 2;
c := b < q.len && q.priority(s[b]) < q.priority(s[a]) ? b : a;
a := i * 2 + 1
b := i * 2 + 2
c := b < q.len && q.priority(s[b]) < q.priority(s[a]) ? b : a
if q.priority(s[c]) >= q.priority(root) do break;
s[i] = s[c];
i = c;
if q.priority(s[c]) >= q.priority(root) do break
s[i] = s[c]
i = c
}
if q.len > 0 do s[i] = root;
return min;
if q.len > 0 do s[i] = root
return min
}
priority_queue_peek :: proc(q: ^$Q/Priority_Queue($T)) -> T {
assert(q.len > 0);
assert(q.len > 0)
s := array_slice(q.data);
return s[0];
s := array_slice(q.data)
return s[0]
}
+58 -58
View File
@@ -32,144 +32,144 @@ queue_consume
*/
queue_init_none :: proc(q: ^$Q/Queue($T), allocator := context.allocator) {
queue_init_len(q, 0, allocator);
queue_init_len(q, 0, allocator)
}
queue_init_len :: proc(q: ^$Q/Queue($T), len: int, allocator := context.allocator) {
queue_init_len_cap(q, 0, 16, allocator);
queue_init_len_cap(q, 0, 16, allocator)
}
queue_init_len_cap :: proc(q: ^$Q/Queue($T), len: int, cap: int, allocator := context.allocator) {
array_init(&q.data, len, cap, allocator);
q.len = len;
q.offset = 0;
array_init(&q.data, len, cap, allocator)
q.len = len
q.offset = 0
}
queue_init :: proc{queue_init_none, queue_init_len, queue_init_len_cap};
queue_init :: proc{queue_init_none, queue_init_len, queue_init_len_cap}
queue_delete :: proc(q: $Q/Queue($T)) {
array_delete(q.data);
array_delete(q.data)
}
queue_clear :: proc(q: ^$Q/Queue($T)) {
q.len = 0;
q.len = 0
}
queue_len :: proc(q: $Q/Queue($T)) -> int {
return q.len;
return q.len
}
queue_cap :: proc(q: $Q/Queue($T)) -> int {
return array_cap(q.data);
return array_cap(q.data)
}
queue_space :: proc(q: $Q/Queue($T)) -> int {
return array_len(q.data) - q.len;
return array_len(q.data) - q.len
}
queue_get :: proc(q: $Q/Queue($T), index: int) -> T {
i := (index + q.offset) % array_len(q.data);
data := array_slice(q.data);
return data[i];
i := (index + q.offset) % array_len(q.data)
data := array_slice(q.data)
return data[i]
}
queue_set :: proc(q: ^$Q/Queue($T), index: int, item: T) {
i := (index + q.offset) % array_len(q.data);
data := array_slice(q.data);
data[i] = item;
i := (index + q.offset) % array_len(q.data)
data := array_slice(q.data)
data[i] = item
}
queue_reserve :: proc(q: ^$Q/Queue($T), capacity: int) {
if capacity > q.len {
_queue_increase_capacity(q, capacity);
_queue_increase_capacity(q, capacity)
}
}
queue_resize :: proc(q: ^$Q/Queue($T), length: int) {
if length > q.len {
_queue_increase_capacity(q, length);
_queue_increase_capacity(q, length)
}
q.len = length;
q.len = length
}
queue_push_back :: proc(q: ^$Q/Queue($T), item: T) {
if queue_space(q^) == 0 {
_queue_grow(q);
_queue_grow(q)
}
queue_set(q, q.len, item);
q.len += 1;
queue_set(q, q.len, item)
q.len += 1
}
queue_push_front :: proc(q: ^$Q/Queue($T), item: T) {
if queue_space(q^) == 0 {
_queue_grow(q);
_queue_grow(q)
}
q.offset = (q.offset - 1 + array_len(q.data)) % array_len(q.data);
q.len += 1;
queue_set(q, 0, item);
q.offset = (q.offset - 1 + array_len(q.data)) % array_len(q.data)
q.len += 1
queue_set(q, 0, item)
}
queue_pop_front :: proc(q: ^$Q/Queue($T)) -> T {
assert(q.len > 0);
item := queue_get(q^, 0);
q.offset = (q.offset + 1) % array_len(q.data);
q.len -= 1;
assert(q.len > 0)
item := queue_get(q^, 0)
q.offset = (q.offset + 1) % array_len(q.data)
q.len -= 1
if q.len == 0 {
q.offset = 0;
q.offset = 0
}
return item;
return item
}
queue_pop_back :: proc(q: ^$Q/Queue($T)) -> T {
assert(q.len > 0);
item := queue_get(q^, q.len-1);
q.len -= 1;
return item;
assert(q.len > 0)
item := queue_get(q^, q.len-1)
q.len -= 1
return item
}
queue_consume :: proc(q: ^$Q/Queue($T), count: int) {
q.offset = (q.offset + count) & array_len(q.data);
q.len -= count;
q.offset = (q.offset + count) & array_len(q.data)
q.len -= count
}
queue_push_elems :: proc(q: ^$Q/Queue($T), items: ..T) {
if queue_space(q^) < len(items) {
_queue_grow(q, q.len + len(items));
_queue_grow(q, q.len + len(items))
}
size := array_len(q.data);
insert := (q.offset + q.len) % size;
size := array_len(q.data)
insert := (q.offset + q.len) % size
to_insert := len(items);
to_insert := len(items)
if insert + to_insert > size {
to_insert = size - insert;
to_insert = size - insert
}
the_items := items[:];
the_items := items[:]
data := array_slice(q.data);
data := array_slice(q.data)
q.len += copy(data[insert:][:to_insert], the_items);
the_items = the_items[to_insert:];
q.len += copy(data[:], the_items);
q.len += copy(data[insert:][:to_insert], the_items)
the_items = the_items[to_insert:]
q.len += copy(data[:], the_items)
}
queue_push :: proc{queue_push_back, queue_push_elems};
queue_push :: proc{queue_push_back, queue_push_elems}
_queue_increase_capacity :: proc(q: ^$Q/Queue($T), new_capacity: int) {
end := array_len(q.data);
array_resize(&q.data, new_capacity);
end := array_len(q.data)
array_resize(&q.data, new_capacity)
if q.offset + q.len > end {
end_items := q.len + end;
data := array_slice(q.data);
copy(data[new_capacity-end_items:][:end_items], data[q.offset:][:end_items]);
q.offset += new_capacity - end;
end_items := q.len + end
data := array_slice(q.data)
copy(data[new_capacity-end_items:][:end_items], data[q.offset:][:end_items])
q.offset += new_capacity - end
}
}
_queue_grow :: proc(q: ^$Q/Queue($T), min_capacity: int = 0) {
new_capacity := max(array_len(q.data)*2 + 8, min_capacity);
_queue_increase_capacity(q, new_capacity);
new_capacity := max(array_len(q.data)*2 + 8, min_capacity)
_queue_increase_capacity(q, new_capacity)
}
+24 -24
View File
@@ -7,68 +7,68 @@ Ring :: struct($T: typeid) {
}
ring_init :: proc(r: ^$R/Ring) -> ^R {
r.prev, r.next = r, r;
return r;
r.prev, r.next = r, r
return r
}
ring_next :: proc(r: ^$R/Ring) -> ^R {
if r.next == nil {
return ring_init(r);
return ring_init(r)
}
return r.next;
return r.next
}
ring_prev :: proc(r: ^$R/Ring) -> ^R {
if r.prev == nil {
return ring_init(r);
return ring_init(r)
}
return r.prev;
return r.prev
}
ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
r := r;
r := r
if r.next == nil {
return ring_init(r);
return ring_init(r)
}
switch {
case n < 0:
for _ in n..<0 {
r = r.prev;
r = r.prev
}
case n > 0:
for _ in 0..<n {
r = r.next;
r = r.next
}
}
return r;
return r
}
ring_link :: proc(r, s: ^$R/Ring) -> ^R {
n := ring_next(r);
n := ring_next(r)
if s != nil {
p := ring_prev(s);
r.next = s;
s.prev = r;
n.prev = p;
p.next = n;
p := ring_prev(s)
r.next = s
s.prev = r
n.prev = p
p.next = n
}
return n;
return n
}
ring_unlink :: proc(r: ^$R/Ring, n: int) -> ^R {
if n <= 0 {
return nil;
return nil
}
return ring_link(r, ring_move(r, n+1));
return ring_link(r, ring_move(r, n+1))
}
ring_len :: proc(r: ^$R/Ring) -> int {
n := 0;
n := 0
if r != nil {
n = 1;
n = 1
for p := ring_next(r); p != r; p = p.next {
n += 1;
n += 1
}
}
return n;
return n
}
+85 -85
View File
@@ -26,88 +26,88 @@ set_reserve
set_clear
*/
set_init :: proc{set_init_none, set_init_cap};
set_init :: proc{set_init_none, set_init_cap}
set_init_none :: proc(m: ^Set, allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
m.hash.allocator = allocator
m.entries.allocator = allocator
}
set_init_cap :: proc(m: ^Set, cap: int, allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
set_reserve(m, cap);
m.hash.allocator = allocator
m.entries.allocator = allocator
set_reserve(m, cap)
}
set_delete :: proc(m: Set) {
array_delete(m.hash);
array_delete(m.entries);
array_delete(m.hash)
array_delete(m.entries)
}
set_in :: proc(m: Set, key: u64) -> bool {
return _set_find_or_fail(m, key) >= 0;
return _set_find_or_fail(m, key) >= 0
}
set_not_in :: proc(m: Set, key: u64) -> bool {
return _set_find_or_fail(m, key) < 0;
return _set_find_or_fail(m, key) < 0
}
set_add :: proc(m: ^Set, key: u64) {
if array_len(m.hash) == 0 {
_set_grow(m);
_set_grow(m)
}
_ = _set_find_or_make(m, key);
_ = _set_find_or_make(m, key)
if _set_full(m^) {
_set_grow(m);
_set_grow(m)
}
}
set_remove :: proc(m: ^Set, key: u64) {
fr := _set_find_key(m^, key);
fr := _set_find_key(m^, key)
if fr.entry_index >= 0 {
_set_erase(m, fr);
_set_erase(m, fr)
}
}
set_reserve :: proc(m: ^Set, new_size: int) {
nm: Set;
set_init(&nm, m.hash.allocator);
array_resize(&nm.hash, new_size);
array_reserve(&nm.entries, array_len(m.entries));
nm: Set
set_init(&nm, m.hash.allocator)
array_resize(&nm.hash, new_size)
array_reserve(&nm.entries, array_len(m.entries))
for i in 0..<new_size {
array_set(&nm.hash, i, -1);
array_set(&nm.hash, i, -1)
}
for i in 0..<array_len(m.entries) {
e := array_get(m.entries, i);
set_add(&nm, e.key);
e := array_get(m.entries, i)
set_add(&nm, e.key)
}
set_delete(m^);
m^ = nm;
set_delete(m^)
m^ = nm
}
set_clear :: proc(m: ^Set) {
array_clear(&m.hash);
array_clear(&m.entries);
array_clear(&m.hash)
array_clear(&m.entries)
}
set_equal :: proc(a, b: Set) -> bool {
a_entries := array_slice(a.entries);
b_entries := array_slice(b.entries);
a_entries := array_slice(a.entries)
b_entries := array_slice(b.entries)
if len(a_entries) != len(b_entries) {
return false;
return false
}
for e in a_entries {
if set_not_in(b, e.key) {
return false;
return false
}
}
return true;
return true
}
@@ -115,126 +115,126 @@ set_equal :: proc(a, b: Set) -> bool {
/// Internal
_set_add_entry :: proc(m: ^Set, key: u64) -> int {
e: Set_Entry;
e.key = key;
e.next = -1;
idx := array_len(m.entries);
array_push(&m.entries, e);
return idx;
e: Set_Entry
e.key = key
e.next = -1
idx := array_len(m.entries)
array_push(&m.entries, e)
return idx
}
_set_erase :: proc(m: ^Set, fr: Map_Find_Result) {
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next);
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next)
} else {
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next;
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next
}
if fr.entry_index == array_len(m.entries)-1 {
array_pop_back(&m.entries);
return;
array_pop_back(&m.entries)
return
}
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1));
last := _set_find_key(m^, array_get(m.entries, fr.entry_index).key);
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1))
last := _set_find_key(m^, array_get(m.entries, fr.entry_index).key)
if last.entry_prev < 0 {
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index;
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index
} else {
array_set(&m.hash, last.hash_index, fr.entry_index);
array_set(&m.hash, last.hash_index, fr.entry_index)
}
}
_set_find_key :: proc(m: Set, key: u64) -> Map_Find_Result {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
fr.entry_index = -1;
fr: Map_Find_Result
fr.hash_index = -1
fr.entry_prev = -1
fr.entry_index = -1
if array_len(m.hash) == 0 {
return fr;
return fr
}
fr.hash_index = int(key % u64(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
fr.hash_index = int(key % u64(array_len(m.hash)))
fr.entry_index = array_get(m.hash, fr.hash_index)
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
it := array_get_ptr(m.entries, fr.entry_index)
if it.key == key {
return fr;
return fr
}
fr.entry_prev = fr.entry_index;
fr.entry_index = it.next;
fr.entry_prev = fr.entry_index
fr.entry_index = it.next
}
return fr;
return fr
}
_set_find_entry :: proc(m: ^Set, e: ^Set_Entry) -> Map_Find_Result {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
fr.entry_index = -1;
fr: Map_Find_Result
fr.hash_index = -1
fr.entry_prev = -1
fr.entry_index = -1
if array_len(m.hash) == 0 {
return fr;
return fr
}
fr.hash_index = int(e.key % u64(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
fr.hash_index = int(e.key % u64(array_len(m.hash)))
fr.entry_index = array_get(m.hash, fr.hash_index)
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
it := array_get_ptr(m.entries, fr.entry_index)
if it == e {
return fr;
return fr
}
fr.entry_prev = fr.entry_index;
fr.entry_index = it.next;
fr.entry_prev = fr.entry_index
fr.entry_index = it.next
}
return fr;
return fr
}
_set_find_or_fail :: proc(m: Set, key: u64) -> int {
return _set_find_key(m, key).entry_index;
return _set_find_key(m, key).entry_index
}
_set_find_or_make :: proc(m: ^Set, key: u64) -> int {
fr := _set_find_key(m^, key);
fr := _set_find_key(m^, key)
if fr.entry_index >= 0 {
return fr.entry_index;
return fr.entry_index
}
i := _set_add_entry(m, key);
i := _set_add_entry(m, key)
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i);
array_set(&m.hash, fr.hash_index, i)
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i;
array_get_ptr(m.entries, fr.entry_prev).next = i
}
return i;
return i
}
_set_make :: proc(m: ^Set, key: u64) -> int {
fr := _set_find_key(m^, key);
i := _set_add_entry(m, key);
fr := _set_find_key(m^, key)
i := _set_add_entry(m, key)
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i);
array_set(&m.hash, fr.hash_index, i)
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i;
array_get_ptr(m.entries, fr.entry_prev).next = i
}
array_get_ptr(m.entries, i).next = fr.entry_index;
array_get_ptr(m.entries, i).next = fr.entry_index
return i;
return i
}
_set_full :: proc(m: Set) -> bool {
// TODO(bill): Determine good max load factor
return array_len(m.entries) >= (array_len(m.hash) / 4)*3;
return array_len(m.entries) >= (array_len(m.hash) / 4)*3
}
_set_grow :: proc(m: ^Set) {
new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate
set_reserve(m, new_size);
new_size := array_len(m.entries) * 4 + 7 // TODO(bill): Determine good grow rate
set_reserve(m, new_size)
}
+35 -35
View File
@@ -7,89 +7,89 @@ Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
small_array_len :: proc(a: $A/Small_Array) -> int {
return a.len;
return a.len
}
small_array_cap :: proc(a: $A/Small_Array) -> int {
return len(a.data);
return len(a.data)
}
small_array_space :: proc(a: $A/Small_Array) -> int {
return len(a.data) - a.len;
return len(a.data) - a.len
}
small_array_slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
return a.data[:a.len];
return a.data[:a.len]
}
small_array_get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
return a.data[index];
return a.data[index]
}
small_array_get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
return &a.data[index];
return &a.data[index]
}
small_array_set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
a.data[index] = item;
a.data[index] = item
}
small_array_resize :: proc(a: ^$A/Small_Array, length: int) {
a.len = min(length, len(a.data));
a.len = min(length, len(a.data))
}
small_array_push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
if a.len < len(a.data) {
a.len += 1;
a.data[a.len-1] = item;
return true;
a.len += 1
a.data[a.len-1] = item
return true
}
return false;
return false
}
small_array_push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
if a.len < len(a.data) {
a.len += 1;
data := small_array_slice(a);
copy(data[1:], data[:]);
data[0] = item;
return true;
a.len += 1
data := small_array_slice(a)
copy(data[1:], data[:])
data[0] = item
return true
}
return false;
return false
}
small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
assert(condition=a.len > 0, loc=loc);
item := a.data[a.len-1];
a.len -= 1;
return item;
assert(condition=a.len > 0, loc=loc)
item := a.data[a.len-1]
a.len -= 1
return item
}
small_array_pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
assert(condition=a.len > 0, loc=loc);
item := a.data[0];
s := small_array_slice(a);
copy(s[:], s[1:]);
a.len -= 1;
return item;
assert(condition=a.len > 0, loc=loc)
item := a.data[0]
s := small_array_slice(a)
copy(s[:], s[1:])
a.len -= 1
return item
}
small_array_consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
assert(condition=a.len >= count, loc=loc);
a.len -= count;
assert(condition=a.len >= count, loc=loc)
a.len -= count
}
small_array_clear :: proc(a: ^$A/Small_Array($N, $T)) {
small_array_resize(a, 0);
small_array_resize(a, 0)
}
small_array_push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
n := copy(a.data[a.len:], items[:]);
a.len += n;
n := copy(a.data[a.len:], items[:])
a.len += n
}
small_array_push :: proc{small_array_push_back, small_array_push_back_elems};
small_array_append :: proc{small_array_push_back, small_array_push_back_elems};
small_array_push :: proc{small_array_push_back, small_array_push_back_elems}
small_array_append :: proc{small_array_push_back, small_array_push_back_elems}
+1 -1
View File
@@ -1,3 +1,3 @@
package dynlib
Library :: distinct rawptr;
Library :: distinct rawptr
+9 -9
View File
@@ -7,19 +7,19 @@ import "core:strings"
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
wide_path := win32.utf8_to_wstring(path, context.temp_allocator);
handle := cast(Library)win32.LoadLibraryW(wide_path);
return handle, handle != nil;
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
handle := cast(Library)win32.LoadLibraryW(wide_path)
return handle, handle != nil
}
unload_library :: proc(library: Library) -> bool {
ok := win32.FreeLibrary(cast(win32.HMODULE)library);
return bool(ok);
ok := win32.FreeLibrary(cast(win32.HMODULE)library)
return bool(ok)
}
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
c_str := strings.clone_to_cstring(symbol, context.temp_allocator);
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str);
found = ptr != nil;
return;
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
found = ptr != nil
return
}
+65 -65
View File
@@ -12,9 +12,9 @@ ENC_TABLE := [32]byte {
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
};
}
PADDING :: '=';
PADDING :: '='
DEC_TABLE := [?]u8 {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -31,118 +31,118 @@ DEC_TABLE := [?]u8 {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
}
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
out_length := (len(data) + 4) / 5 * 8;
out := make([]byte, out_length);
_encode(out, data);
return string(out);
out_length := (len(data) + 4) / 5 * 8
out := make([]byte, out_length)
_encode(out, data)
return string(out)
}
@private
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
out := out;
data := data;
out := out
data := data
for len(data) > 0 {
carry: byte;
carry: byte
switch len(data) {
case:
out[7] = ENC_TABLE[data[4] & 0x1f];
carry = data[4] >> 5;
fallthrough;
out[7] = ENC_TABLE[data[4] & 0x1f]
carry = data[4] >> 5
fallthrough
case 4:
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f];
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f];
carry = data[3] >> 7;
fallthrough;
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
carry = data[3] >> 7
fallthrough
case 3:
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f];
carry = (data[2] >> 4) & 0x1f;
fallthrough;
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
carry = (data[2] >> 4) & 0x1f
fallthrough
case 2:
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f];
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f];
carry = (data[1] >> 6) & 0x1f;
fallthrough;
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
carry = (data[1] >> 6) & 0x1f
fallthrough
case 1:
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f];
out[0] = ENC_TABLE[data[0] >> 3];
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
out[0] = ENC_TABLE[data[0] >> 3]
}
if len(data) < 5 {
out[7] = byte(PADDING);
out[7] = byte(PADDING)
if len(data) < 4 {
out[6] = byte(PADDING);
out[5] = byte(PADDING);
out[6] = byte(PADDING)
out[5] = byte(PADDING)
if len(data) < 3 {
out[4] = byte(PADDING);
out[4] = byte(PADDING)
if len(data) < 2 {
out[3] = byte(PADDING);
out[2] = byte(PADDING);
out[3] = byte(PADDING)
out[2] = byte(PADDING)
}
}
}
break;
break
}
data = data[5:];
out = out[8:];
data = data[5:]
out = out[8:]
}
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
if len(data) == 0 {
return nil;
return nil
}
outi := 0;
data := data;
outi := 0
data := data
out := make([]byte, len(data) / 8 * 5, allocator);
end := false;
out := make([]byte, len(data) / 8 * 5, allocator)
end := false
for len(data) > 0 && !end {
dbuf : [8]byte;
dlen := 8;
dbuf : [8]byte
dlen := 8
for j := 0; j < 8; {
if len(data) == 0 {
dlen, end = j, true;
break;
dlen, end = j, true
break
}
input := data[0];
data = data[1:];
input := data[0]
data = data[1:]
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
assert(!(len(data) + j < 8 - 1), "Corrupted input");
assert(!(len(data) + j < 8 - 1), "Corrupted input")
for k := 0; k < 8-1-j; k +=1 {
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
}
dlen, end = j, true;
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input");
break;
dlen, end = j, true
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
break
}
dbuf[j] = DEC_TABLE[input];
assert(dbuf[j] != 0xff, "Corrupted input");
j += 1;
dbuf[j] = DEC_TABLE[input]
assert(dbuf[j] != 0xff, "Corrupted input")
j += 1
}
switch dlen {
case 8:
out[outi + 4] = dbuf[6] << 5 | dbuf[7];
fallthrough;
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
fallthrough
case 7:
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3;
fallthrough;
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
fallthrough
case 5:
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1;
fallthrough;
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
fallthrough
case 4:
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4;
fallthrough;
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
fallthrough
case 2:
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2;
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
}
outi += 5;
outi += 5
}
return out;
return out
}
+35 -35
View File
@@ -16,9 +16,9 @@ ENC_TABLE := [64]byte {
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',
};
}
PADDING :: '=';
PADDING :: '='
DEC_TABLE := [128]int {
-1, -1, -1, -1, -1, -1, -1, -1,
@@ -37,61 +37,61 @@ DEC_TABLE := [128]int {
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, -1, -1, -1, -1, -1,
};
}
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
length := len(data);
length := len(data)
if length == 0 {
return "";
return ""
}
out_length := ((4 * length / 3) + 3) &~ 3;
out := make([]byte, out_length, allocator);
out_length := ((4 * length / 3) + 3) &~ 3
out := make([]byte, out_length, allocator)
c0, c1, c2, block: int;
c0, c1, c2, block: int
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
c0, c1, c2 = int(data[i]), -1, -1;
c0, c1, c2 = int(data[i]), -1, -1
if i + 1 < length { c1 = int(data[i + 1]); }
if i + 2 < length { c2 = int(data[i + 2]); }
if i + 1 < length { c1 = int(data[i + 1]) }
if i + 2 < length { c2 = int(data[i + 2]) }
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0);
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0)
out[d] = ENC_TBL[block >> 18 & 63];
out[d + 1] = ENC_TBL[block >> 12 & 63];
out[d + 2] = c1 == -1 ? PADDING : ENC_TBL[block >> 6 & 63];
out[d + 3] = c2 == -1 ? PADDING : ENC_TBL[block & 63];
out[d] = ENC_TBL[block >> 18 & 63]
out[d + 1] = ENC_TBL[block >> 12 & 63]
out[d + 2] = c1 == -1 ? PADDING : ENC_TBL[block >> 6 & 63]
out[d + 3] = c2 == -1 ? PADDING : ENC_TBL[block & 63]
}
return string(out);
return string(out)
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check {
length := len(data);
length := len(data)
if length == 0 {
return nil;
return nil
}
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0;
out_length := ((length * 6) >> 3) - pad_count;
out := make([]byte, out_length, allocator);
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0
out_length := ((length * 6) >> 3) - pad_count
out := make([]byte, out_length, allocator)
c0, c1, c2, c3: int;
b0, b1, b2: int;
c0, c1, c2, c3: int
b0, b1, b2: int
for i, j := 0, 0; i < length; i, j = i + 4, j + 3 {
c0 = DEC_TBL[data[i]];
c1 = DEC_TBL[data[i + 1]];
c2 = DEC_TBL[data[i + 2]];
c3 = DEC_TBL[data[i + 3]];
c0 = DEC_TBL[data[i]]
c1 = DEC_TBL[data[i + 1]]
c2 = DEC_TBL[data[i + 2]]
c3 = DEC_TBL[data[i + 3]]
b0 = (c0 << 2) | (c1 >> 4);
b1 = (c1 << 4) | (c2 >> 2);
b2 = (c2 << 6) | c3;
b0 = (c0 << 2) | (c1 >> 4)
b1 = (c1 << 4) | (c2 >> 2)
b2 = (c2 << 6) | c3
out[j] = byte(b0);
out[j + 1] = byte(b1);
out[j + 2] = byte(b2);
out[j] = byte(b0)
out[j + 1] = byte(b1)
out[j + 2] = byte(b2)
}
return out;
return out
}
+136 -136
View File
@@ -68,7 +68,7 @@ reader_error_kind_string := [Reader_Error_Kind]string{
.Quote = "extra or missing \" in quoted field",
.Field_Count = "wrong field count",
.Invalid_Delim = "invalid delimiter",
};
}
Reader_Error :: struct {
kind: Reader_Error_Kind,
@@ -83,35 +83,35 @@ Error :: union {
io.Error,
}
DEFAULT_RECORD_BUFFER_CAPACITY :: 256;
DEFAULT_RECORD_BUFFER_CAPACITY :: 256
// reader_init initializes a new Reader from r
reader_init :: proc(reader: ^Reader, r: io.Reader, buffer_allocator := context.allocator) {
reader.comma = ',';
reader.comma = ','
context.allocator = buffer_allocator;
reserve(&reader.record_buffer, DEFAULT_RECORD_BUFFER_CAPACITY);
reserve(&reader.raw_buffer, 0);
reserve(&reader.field_indices, 0);
reserve(&reader.last_record, 0);
bufio.reader_init(&reader.r, r);
context.allocator = buffer_allocator
reserve(&reader.record_buffer, DEFAULT_RECORD_BUFFER_CAPACITY)
reserve(&reader.raw_buffer, 0)
reserve(&reader.field_indices, 0)
reserve(&reader.last_record, 0)
bufio.reader_init(&reader.r, r)
}
// reader_init_with_string initializes a new Reader from s
reader_init_with_string :: proc(reader: ^Reader, s: string, buffer_allocator := context.allocator) {
strings.reader_init(&reader.sr, s);
r, _ := io.to_reader(strings.reader_to_stream(&reader.sr));
reader_init(reader, r, buffer_allocator);
strings.reader_init(&reader.sr, s)
r, _ := io.to_reader(strings.reader_to_stream(&reader.sr))
reader_init(reader, r, buffer_allocator)
}
// reader_destroy destroys a Reader
reader_destroy :: proc(r: ^Reader) {
delete(r.raw_buffer);
delete(r.record_buffer);
delete(r.field_indices);
delete(r.last_record);
bufio.reader_destroy(&r.r);
delete(r.raw_buffer)
delete(r.record_buffer)
delete(r.field_indices)
delete(r.last_record)
bufio.reader_destroy(&r.r)
}
// read reads a single record (a slice of fields) from r
@@ -119,21 +119,21 @@ reader_destroy :: proc(r: ^Reader) {
// All \r\n sequences are normalized to \n, including multi-line field
read :: proc(r: ^Reader, allocator := context.allocator) -> (record: []string, err: Error) {
if r.reuse_record {
record, err = _read_record(r, &r.last_record, allocator);
resize(&r.last_record, len(record));
copy(r.last_record[:], record);
record, err = _read_record(r, &r.last_record, allocator)
resize(&r.last_record, len(record))
copy(r.last_record[:], record)
} else {
record, err = _read_record(r, nil, allocator);
record, err = _read_record(r, nil, allocator)
}
return;
return
}
// is_io_error checks where an Error is a specific io.Error kind
is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
if v, ok := err.(io.Error); ok {
return v == io_err;
return v == io_err
}
return false;
return false
}
@@ -141,97 +141,97 @@ is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
// Each record is a slice of fields.
// read_all is defined to read until an EOF, and does not treat, and does not treat EOF as an error
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
context.allocator = allocator;
records: [dynamic][]string;
context.allocator = allocator
records: [dynamic][]string
for {
record, rerr := _read_record(r, nil, allocator);
record, rerr := _read_record(r, nil, allocator)
if is_io_error(rerr, .EOF) {
return records[:], nil;
return records[:], nil
}
if rerr != nil {
return nil, rerr;
return nil, rerr
}
append(&records, record);
append(&records, record)
}
}
// read reads a single record (a slice of fields) from the provided input.
read_from_string :: proc(input: string, record_allocator := context.allocator, buffer_allocator := context.allocator) -> (record: []string, n: int, err: Error) {
ir: strings.Reader;
strings.reader_init(&ir, input);
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir));
ir: strings.Reader
strings.reader_init(&ir, input)
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir))
r: Reader;
reader_init(&r, input_reader, buffer_allocator);
defer reader_destroy(&r);
record, err = read(&r, record_allocator);
n = int(r.r.r);
return;
r: Reader
reader_init(&r, input_reader, buffer_allocator)
defer reader_destroy(&r)
record, err = read(&r, record_allocator)
n = int(r.r.r)
return
}
// read_all reads all the remaining records from the provided input.
read_all_from_string :: proc(input: string, records_allocator := context.allocator, buffer_allocator := context.allocator) -> ([][]string, Error) {
ir: strings.Reader;
strings.reader_init(&ir, input);
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir));
ir: strings.Reader
strings.reader_init(&ir, input)
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir))
r: Reader;
reader_init(&r, input_reader, buffer_allocator);
defer reader_destroy(&r);
return read_all(&r, records_allocator);
r: Reader
reader_init(&r, input_reader, buffer_allocator)
defer reader_destroy(&r)
return read_all(&r, records_allocator)
}
@private
is_valid_delim :: proc(r: rune) -> bool {
switch r {
case 0, '"', '\r', '\n', utf8.RUNE_ERROR:
return false;
return false
}
return utf8.valid_rune(r);
return utf8.valid_rune(r)
}
@private
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
line, err := bufio.reader_read_slice(&r.r, '\n');
line, err := bufio.reader_read_slice(&r.r, '\n')
if err == .Buffer_Full {
clear(&r.raw_buffer);
append(&r.raw_buffer, ..line);
clear(&r.raw_buffer)
append(&r.raw_buffer, ..line)
for err == .Buffer_Full {
line, err = bufio.reader_read_slice(&r.r, '\n');
append(&r.raw_buffer, ..line);
line, err = bufio.reader_read_slice(&r.r, '\n')
append(&r.raw_buffer, ..line)
}
line = r.raw_buffer[:];
line = r.raw_buffer[:]
}
if len(line) > 0 && err == .EOF {
err = nil;
err = nil
if line[len(line)-1] == '\r' {
line = line[:len(line)-1];
line = line[:len(line)-1]
}
}
r.line_count += 1;
r.line_count += 1
// normalize \r\n to \n
n := len(line);
n := len(line)
for n >= 2 && string(line[n-2:]) == "\r\n" {
line[n-2] = '\n';
line = line[:n-1];
line[n-2] = '\n'
line = line[:n-1]
}
return line, err;
return line, err
}
length_newline :: proc(b: []byte) -> int {
if len(b) > 0 && b[len(b)-1] == '\n' {
return 1;
return 1
}
return 0;
return 0
}
next_rune :: proc(b: []byte) -> rune {
r, _ := utf8.decode_rune(b);
return r;
r, _ := utf8.decode_rune(b)
return r
}
if r.comma == r.comment ||
@@ -240,152 +240,152 @@ _read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.all
err := Reader_Error{
kind = .Invalid_Delim,
line = r.line_count,
};
return nil, err;
}
return nil, err
}
line, full_line: []byte;
err_read: io.Error;
line, full_line: []byte
err_read: io.Error
for err_read == nil {
line, err_read = read_line(r);
line, err_read = read_line(r)
if r.comment != 0 && next_rune(line) == r.comment {
line = nil;
continue;
line = nil
continue
}
if err_read == nil && len(line) == length_newline(line) {
line = nil;
continue;
line = nil
continue
}
full_line = line;
break;
full_line = line
break
}
if is_io_error(err_read, .EOF) {
return nil, err_read;
return nil, err_read
}
err: Error;
quote_len :: len(`"`);
comma_len := utf8.rune_size(r.comma);
record_line := r.line_count;
clear(&r.record_buffer);
clear(&r.field_indices);
err: Error
quote_len :: len(`"`)
comma_len := utf8.rune_size(r.comma)
record_line := r.line_count
clear(&r.record_buffer)
clear(&r.field_indices)
parse_field: for {
if r.trim_leading_space {
line = bytes.trim_left_space(line);
line = bytes.trim_left_space(line)
}
if len(line) == 0 || line[0] != '"' {
i := bytes.index_rune(line, r.comma);
field := line;
i := bytes.index_rune(line, r.comma)
field := line
if i >= 0 {
field = field[:i];
field = field[:i]
} else {
field = field[:len(field) - length_newline(field)];
field = field[:len(field) - length_newline(field)]
}
if !r.lazy_quotes {
if j := bytes.index_byte(field, '"'); j >= 0 {
column := utf8.rune_count(full_line[:len(full_line) - len(line[j:])]);
column := utf8.rune_count(full_line[:len(full_line) - len(line[j:])])
err = Reader_Error{
kind = .Bare_Quote,
start_line = record_line,
line = r.line_count,
column = column,
};
break parse_field;
}
break parse_field
}
}
append(&r.record_buffer, ..field);
append(&r.field_indices, len(r.record_buffer));
append(&r.record_buffer, ..field)
append(&r.field_indices, len(r.record_buffer))
if i >= 0 {
line = line[i+comma_len:];
continue parse_field;
line = line[i+comma_len:]
continue parse_field
}
break parse_field;
break parse_field
} else {
line = line[quote_len:];
line = line[quote_len:]
for {
i := bytes.index_byte(line, '"');
i := bytes.index_byte(line, '"')
switch {
case i >= 0:
append(&r.record_buffer, ..line[:i]);
line = line[i+quote_len:];
append(&r.record_buffer, ..line[:i])
line = line[i+quote_len:]
switch ch := next_rune(line); {
case ch == '"': // append quote
append(&r.record_buffer, '"');
line = line[quote_len:];
append(&r.record_buffer, '"')
line = line[quote_len:]
case ch == r.comma: // end of field
line = line[comma_len:];
append(&r.field_indices, len(r.record_buffer));
continue parse_field;
line = line[comma_len:]
append(&r.field_indices, len(r.record_buffer))
continue parse_field
case length_newline(line) == len(line): // end of line
append(&r.field_indices, len(r.record_buffer));
break parse_field;
append(&r.field_indices, len(r.record_buffer))
break parse_field
case r.lazy_quotes: // bare quote
append(&r.record_buffer, '"');
append(&r.record_buffer, '"')
case: // invalid non-escaped quote
column := utf8.rune_count(full_line[:len(full_line) - len(line) - quote_len]);
column := utf8.rune_count(full_line[:len(full_line) - len(line) - quote_len])
err = Reader_Error{
kind = .Quote,
start_line = record_line,
line = r.line_count,
column = column,
};
break parse_field;
}
break parse_field
}
case len(line) > 0:
append(&r.record_buffer, ..line);
append(&r.record_buffer, ..line)
if err_read != nil {
break parse_field;
break parse_field
}
line, err_read = read_line(r);
line, err_read = read_line(r)
if is_io_error(err_read, .EOF) {
err_read = nil;
err_read = nil
}
full_line = line;
full_line = line
case:
if !r.lazy_quotes && err_read == nil {
column := utf8.rune_count(full_line);
column := utf8.rune_count(full_line)
err = Reader_Error{
kind = .Quote,
start_line = record_line,
line = r.line_count,
column = column,
};
break parse_field;
}
break parse_field
}
append(&r.field_indices, len(r.record_buffer));
break parse_field;
append(&r.field_indices, len(r.record_buffer))
break parse_field
}
}
}
}
if err == nil && err_read != nil {
err = err_read;
err = err_read
}
context.allocator = allocator;
dst := dst;
str := string(r.record_buffer[:]);
context.allocator = allocator
dst := dst
str := string(r.record_buffer[:])
if dst == nil {
// use local variable
dst = &([dynamic]string){};
dst = &([dynamic]string){}
}
clear(dst);
resize(dst, len(r.field_indices));
pre_idx: int;
clear(dst)
resize(dst, len(r.field_indices))
pre_idx: int
for idx, i in r.field_indices {
field := str[pre_idx:idx];
field := str[pre_idx:idx]
if !r.reuse_record_buffer {
field = strings.clone(field);
field = strings.clone(field)
}
dst[i] = field;
pre_idx = idx;
dst[i] = field
pre_idx = idx
}
if r.fields_per_record > 0 {
@@ -396,11 +396,11 @@ _read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.all
line = r.line_count,
expected = r.fields_per_record,
got = len(dst),
};
}
}
} else if r.fields_per_record == 0 {
r.fields_per_record = len(dst);
r.fields_per_record = len(dst)
}
return dst[:], err;
return dst[:], err
}
+32 -32
View File
@@ -17,8 +17,8 @@ Writer :: struct {
// writer_init initializes a Writer that writes to w
writer_init :: proc(writer: ^Writer, w: io.Writer) {
writer.comma = ',';
writer.w = w;
writer.comma = ','
writer.w = w
}
// write writes a single CSV records to w with any of the necessarily quoting.
@@ -26,101 +26,101 @@ writer_init :: proc(writer: ^Writer, w: io.Writer) {
//
// If the underlying io.Writer requires flushing, make sure to call io.flush
write :: proc(w: ^Writer, record: []string) -> io.Error {
CHAR_SET :: "\n\r\"";
CHAR_SET :: "\n\r\""
field_needs_quoting :: proc(w: ^Writer, field: string) -> bool {
switch {
case field == "": // No need to quote empty strings
return false;
return false
case field == `\.`: // Postgres is weird
return true;
return true
case w.comma < utf8.RUNE_SELF: // ASCII optimization
for i in 0..<len(field) {
switch field[i] {
case '\n', '\r', '"', byte(w.comma):
return true;
return true
}
}
case:
if strings.contains_rune(field, w.comma) >= 0 {
return true;
return true
}
if strings.contains_any(field, CHAR_SET) {
return true;
return true
}
}
// Leading spaces need quoting
r, _ := utf8.decode_rune_in_string(field);
return strings.is_space(r);
r, _ := utf8.decode_rune_in_string(field)
return strings.is_space(r)
}
if !is_valid_delim(w.comma) {
return .No_Progress; // TODO(bill): Is this a good error?
return .No_Progress // TODO(bill): Is this a good error?
}
for _, field_idx in record {
// NOTE(bill): declared like this so that the field can be modified later if necessary
field := record[field_idx];
field := record[field_idx]
if field_idx > 0 {
io.write_rune(w.w, w.comma) or_return;
io.write_rune(w.w, w.comma) or_return
}
if !field_needs_quoting(w, field) {
io.write_string(w.w, field) or_return;
continue;
io.write_string(w.w, field) or_return
continue
}
io.write_byte(w.w, '"') or_return;
io.write_byte(w.w, '"') or_return
for len(field) > 0 {
i := strings.index_any(field, CHAR_SET);
i := strings.index_any(field, CHAR_SET)
if i < 0 {
i = len(field);
i = len(field)
}
io.write_string(w.w, field[:i]) or_return;
field = field[i:];
io.write_string(w.w, field[:i]) or_return
field = field[i:]
if len(field) > 0 {
switch field[0] {
case '\r':
if !w.use_crlf {
io.write_byte(w.w, '\r') or_return;
io.write_byte(w.w, '\r') or_return
}
case '\n':
if w.use_crlf {
io.write_string(w.w, "\r\n") or_return;
io.write_string(w.w, "\r\n") or_return
} else {
io.write_byte(w.w, '\n') or_return;
io.write_byte(w.w, '\n') or_return
}
case '"':
io.write_string(w.w, `""`) or_return;
io.write_string(w.w, `""`) or_return
}
field = field[1:];
field = field[1:]
}
}
io.write_byte(w.w, '"') or_return;
io.write_byte(w.w, '"') or_return
}
if w.use_crlf {
_, err := io.write_string(w.w, "\r\n");
return err;
_, err := io.write_string(w.w, "\r\n")
return err
}
return io.write_byte(w.w, '\n');
return io.write_byte(w.w, '\n')
}
// write_all writes multiple CSV records to w using write, and then flushes (if necessary).
write_all :: proc(w: ^Writer, records: [][]string) -> io.Error {
for record in records {
write(w, record) or_return;
write(w, record) or_return
}
return writer_flush(w);
return writer_flush(w)
}
// writer_flush flushes the underlying io.Writer.
// If the underlying io.Writer does not support flush, nil is returned.
writer_flush :: proc(w: ^Writer) -> io.Error {
return io.flush(auto_cast w.w);
return io.flush(auto_cast w.w)
}
+46 -46
View File
@@ -2,10 +2,10 @@ package encoding_hxa
import "core:mem"
LATEST_VERSION :: 3;
VERSION_API :: "0.3";
LATEST_VERSION :: 3
VERSION_API :: "0.3"
MAGIC_NUMBER :: 'H'<<0 | 'x'<<8 | 'A'<<16 | '\x00'<<24;
MAGIC_NUMBER :: 'H'<<0 | 'x'<<8 | 'A'<<16 | '\x00'<<24
Header :: struct #packed {
magic_number: u32le,
@@ -48,7 +48,7 @@ Meta_Value_Type :: enum u8 {
Text = 3,
Binary = 4,
Meta = 5,
};
}
Meta :: struct {
name: string, // name of the meta data value (maximum length is 255)
@@ -74,7 +74,7 @@ Layer :: struct {
}
// Layers stacks are arrays of layers where all the layers have the same number of entries (polygons, edges, vertices or pixels)
Layer_Stack :: distinct []Layer;
Layer_Stack :: distinct []Layer
Node_Geometry :: struct {
vertex_count: u32le, // number of vertices
@@ -92,7 +92,7 @@ Node_Image :: struct {
image_stack: Layer_Stack,
}
Node_Index :: distinct u32le;
Node_Index :: distinct u32le
// A file consists of an array of nodes, All nodes have meta data. Geometry nodes have geometry, image nodes have pixels
Node :: struct {
@@ -114,15 +114,15 @@ If you use HxA for something not covered by the conventions but need a conventio
/* Hard conventions */
/* ---------------- */
CONVENTION_HARD_BASE_VERTEX_LAYER_NAME :: "vertex";
CONVENTION_HARD_BASE_VERTEX_LAYER_ID :: 0;
CONVENTION_HARD_BASE_VERTEX_LAYER_COMPONENTS :: 3;
CONVENTION_HARD_BASE_CORNER_LAYER_NAME :: "reference";
CONVENTION_HARD_BASE_CORNER_LAYER_ID :: 0;
CONVENTION_HARD_BASE_CORNER_LAYER_COMPONENTS :: 1;
CONVENTION_HARD_BASE_CORNER_LAYER_TYPE :: Layer_Data_Type.Int32;
CONVENTION_HARD_EDGE_NEIGHBOUR_LAYER_NAME :: "neighbour";
CONVENTION_HARD_EDGE_NEIGHBOUR_LAYER_TYPE :: Layer_Data_Type.Int32;
CONVENTION_HARD_BASE_VERTEX_LAYER_NAME :: "vertex"
CONVENTION_HARD_BASE_VERTEX_LAYER_ID :: 0
CONVENTION_HARD_BASE_VERTEX_LAYER_COMPONENTS :: 3
CONVENTION_HARD_BASE_CORNER_LAYER_NAME :: "reference"
CONVENTION_HARD_BASE_CORNER_LAYER_ID :: 0
CONVENTION_HARD_BASE_CORNER_LAYER_COMPONENTS :: 1
CONVENTION_HARD_BASE_CORNER_LAYER_TYPE :: Layer_Data_Type.Int32
CONVENTION_HARD_EDGE_NEIGHBOUR_LAYER_NAME :: "neighbour"
CONVENTION_HARD_EDGE_NEIGHBOUR_LAYER_TYPE :: Layer_Data_Type.Int32
@@ -131,63 +131,63 @@ CONVENTION_HARD_EDGE_NEIGHBOUR_LAYER_TYPE :: Layer_Data_Type.Int32;
/* geometry layers */
CONVENTION_SOFT_LAYER_SEQUENCE0 :: "sequence";
CONVENTION_SOFT_LAYER_NAME_UV0 :: "uv";
CONVENTION_SOFT_LAYER_NORMALS :: "normal";
CONVENTION_SOFT_LAYER_BINORMAL :: "binormal";
CONVENTION_SOFT_LAYER_TANGENT :: "tangent";
CONVENTION_SOFT_LAYER_COLOR :: "color";
CONVENTION_SOFT_LAYER_CREASES :: "creases";
CONVENTION_SOFT_LAYER_SELECTION :: "select";
CONVENTION_SOFT_LAYER_SKIN_WEIGHT :: "skining_weight";
CONVENTION_SOFT_LAYER_SKIN_REFERENCE :: "skining_reference";
CONVENTION_SOFT_LAYER_BLENDSHAPE :: "blendshape";
CONVENTION_SOFT_LAYER_ADD_BLENDSHAPE :: "addblendshape";
CONVENTION_SOFT_LAYER_MATERIAL_ID :: "material";
CONVENTION_SOFT_LAYER_SEQUENCE0 :: "sequence"
CONVENTION_SOFT_LAYER_NAME_UV0 :: "uv"
CONVENTION_SOFT_LAYER_NORMALS :: "normal"
CONVENTION_SOFT_LAYER_BINORMAL :: "binormal"
CONVENTION_SOFT_LAYER_TANGENT :: "tangent"
CONVENTION_SOFT_LAYER_COLOR :: "color"
CONVENTION_SOFT_LAYER_CREASES :: "creases"
CONVENTION_SOFT_LAYER_SELECTION :: "select"
CONVENTION_SOFT_LAYER_SKIN_WEIGHT :: "skining_weight"
CONVENTION_SOFT_LAYER_SKIN_REFERENCE :: "skining_reference"
CONVENTION_SOFT_LAYER_BLENDSHAPE :: "blendshape"
CONVENTION_SOFT_LAYER_ADD_BLENDSHAPE :: "addblendshape"
CONVENTION_SOFT_LAYER_MATERIAL_ID :: "material"
/* Image layers */
CONVENTION_SOFT_ALBEDO :: "albedo";
CONVENTION_SOFT_LIGHT :: "light";
CONVENTION_SOFT_DISPLACEMENT :: "displacement";
CONVENTION_SOFT_DISTORTION :: "distortion";
CONVENTION_SOFT_AMBIENT_OCCLUSION :: "ambient_occlusion";
CONVENTION_SOFT_ALBEDO :: "albedo"
CONVENTION_SOFT_LIGHT :: "light"
CONVENTION_SOFT_DISPLACEMENT :: "displacement"
CONVENTION_SOFT_DISTORTION :: "distortion"
CONVENTION_SOFT_AMBIENT_OCCLUSION :: "ambient_occlusion"
/* tags layers */
CONVENTION_SOFT_NAME :: "name";
CONVENTION_SOFT_TRANSFORM :: "transform";
CONVENTION_SOFT_NAME :: "name"
CONVENTION_SOFT_TRANSFORM :: "transform"
/* destroy procedures */
meta_destroy :: proc(meta: Meta, allocator := context.allocator) {
if nested, ok := meta.value.([]Meta); ok {
for m in nested {
meta_destroy(m);
meta_destroy(m)
}
delete(nested, allocator);
delete(nested, allocator)
}
}
nodes_destroy :: proc(nodes: []Node, allocator := context.allocator) {
for node in nodes {
for meta in node.meta_data {
meta_destroy(meta);
meta_destroy(meta)
}
delete(node.meta_data, allocator);
delete(node.meta_data, allocator)
switch n in node.content {
case Node_Geometry:
delete(n.corner_stack, allocator);
delete(n.edge_stack, allocator);
delete(n.face_stack, allocator);
delete(n.corner_stack, allocator)
delete(n.edge_stack, allocator)
delete(n.face_stack, allocator)
case Node_Image:
delete(n.image_stack, allocator);
delete(n.image_stack, allocator)
}
}
delete(nodes, allocator);
delete(nodes, allocator)
}
file_destroy :: proc(file: File) {
nodes_destroy(file.nodes, file.allocator);
delete(file.backing, file.allocator);
nodes_destroy(file.nodes, file.allocator)
delete(file.backing, file.allocator)
}
+101 -101
View File
@@ -12,20 +12,20 @@ Read_Error :: enum {
}
read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) {
context.allocator = allocator;
context.allocator = allocator
data, ok := os.read_entire_file(filename);
data, ok := os.read_entire_file(filename)
if !ok {
err = .Unable_To_Read_File;
return;
err = .Unable_To_Read_File
return
}
defer if !ok {
delete(data);
delete(data)
} else {
file.backing = data;
file.backing = data
}
file, err = read(data, filename, print_error, allocator);
return;
file, err = read(data, filename, print_error, allocator)
return
}
read :: proc(data: []byte, filename := "<input>", print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) {
@@ -34,182 +34,182 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
data: []byte,
offset: int,
print_error: bool,
};
}
read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) {
remaining := len(r.data) - r.offset;
remaining := len(r.data) - r.offset
if remaining < size_of(T) {
err = .Short_Read;
return;
err = .Short_Read
return
}
ptr := raw_data(r.data[r.offset:]);
value = (^T)(ptr)^;
r.offset += size_of(T);
return;
ptr := raw_data(r.data[r.offset:])
value = (^T)(ptr)^
r.offset += size_of(T)
return
}
read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) {
remaining := len(r.data) - r.offset;
remaining := len(r.data) - r.offset
if remaining < size_of(T)*count {
err = .Short_Read;
return;
err = .Short_Read
return
}
ptr := raw_data(r.data[r.offset:]);
ptr := raw_data(r.data[r.offset:])
value = mem.slice_ptr((^T)(ptr), count);
r.offset += size_of(T)*count;
return;
value = mem.slice_ptr((^T)(ptr), count)
r.offset += size_of(T)*count
return
}
read_string :: proc(r: ^Reader, count: int) -> (string, Read_Error) {
buf, err := read_array(r, byte, count);
return string(buf), err;
buf, err := read_array(r, byte, count)
return string(buf), err
}
read_name :: proc(r: ^Reader) -> (value: string, err: Read_Error) {
len := read_value(r, u8) or_return;
data := read_array(r, byte, int(len)) or_return;
return string(data[:len]), nil;
len := read_value(r, u8) or_return
data := read_array(r, byte, int(len)) or_return
return string(data[:len]), nil
}
read_meta :: proc(r: ^Reader, capacity: u32le) -> (meta_data: []Meta, err: Read_Error) {
meta_data = make([]Meta, int(capacity));
count := 0;
defer meta_data = meta_data[:count];
meta_data = make([]Meta, int(capacity))
count := 0
defer meta_data = meta_data[:count]
for m in &meta_data {
m.name = read_name(r) or_return;
m.name = read_name(r) or_return
type := read_value(r, Meta_Value_Type) or_return;
type := read_value(r, Meta_Value_Type) or_return
if type > max(Meta_Value_Type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type)));
fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type)))
}
err = .Invalid_Data;
return;
err = .Invalid_Data
return
}
array_length := read_value(r, u32le) or_return;
array_length := read_value(r, u32le) or_return
switch type {
case .Int64: m.value = read_array(r, i64le, int(array_length)) or_return;
case .Double: m.value = read_array(r, f64le, int(array_length)) or_return;
case .Node: m.value = read_array(r, Node_Index, int(array_length)) or_return;
case .Text: m.value = read_string(r, int(array_length)) or_return;
case .Binary: m.value = read_array(r, byte, int(array_length)) or_return;
case .Meta: m.value = read_meta(r, array_length) or_return;
case .Int64: m.value = read_array(r, i64le, int(array_length)) or_return
case .Double: m.value = read_array(r, f64le, int(array_length)) or_return
case .Node: m.value = read_array(r, Node_Index, int(array_length)) or_return
case .Text: m.value = read_string(r, int(array_length)) or_return
case .Binary: m.value = read_array(r, byte, int(array_length)) or_return
case .Meta: m.value = read_meta(r, array_length) or_return
}
count += 1;
count += 1
}
return;
return
}
read_layer_stack :: proc(r: ^Reader, capacity: u32le) -> (layers: Layer_Stack, err: Read_Error) {
stack_count := read_value(r, u32le) or_return;
layer_count := 0;
layers = make(Layer_Stack, stack_count);
defer layers = layers[:layer_count];
stack_count := read_value(r, u32le) or_return
layer_count := 0
layers = make(Layer_Stack, stack_count)
defer layers = layers[:layer_count]
for layer in &layers {
layer.name = read_name(r) or_return;
layer.components = read_value(r, u8) or_return;
type := read_value(r, Layer_Data_Type) or_return;
layer.name = read_name(r) or_return
layer.components = read_value(r, u8) or_return
type := read_value(r, Layer_Data_Type) or_return
if type > max(type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type)));
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type)))
}
err = .Invalid_Data;
return;
err = .Invalid_Data
return
}
data_len := int(layer.components) * int(capacity);
data_len := int(layer.components) * int(capacity)
switch type {
case .Uint8: layer.data = read_array(r, u8, data_len) or_return;
case .Int32: layer.data = read_array(r, i32le, data_len) or_return;
case .Float: layer.data = read_array(r, f32le, data_len) or_return;
case .Double: layer.data = read_array(r, f64le, data_len) or_return;
case .Uint8: layer.data = read_array(r, u8, data_len) or_return
case .Int32: layer.data = read_array(r, i32le, data_len) or_return
case .Float: layer.data = read_array(r, f32le, data_len) or_return
case .Double: layer.data = read_array(r, f64le, data_len) or_return
}
layer_count += 1;
layer_count += 1
}
return;
return
}
if len(data) < size_of(Header) {
return;
return
}
context.allocator = allocator;
context.allocator = allocator
header := cast(^Header)raw_data(data);
assert(header.magic_number == MAGIC_NUMBER);
header := cast(^Header)raw_data(data)
assert(header.magic_number == MAGIC_NUMBER)
r := &Reader{
filename = filename,
data = data[:],
offset = size_of(Header),
print_error = print_error,
};
node_count := 0;
file.nodes = make([]Node, header.internal_node_count);
defer if err != nil {
nodes_destroy(file.nodes);
file.nodes = nil;
}
defer file.nodes = file.nodes[:node_count];
node_count := 0
file.nodes = make([]Node, header.internal_node_count)
defer if err != nil {
nodes_destroy(file.nodes)
file.nodes = nil
}
defer file.nodes = file.nodes[:node_count]
for node_idx in 0..<header.internal_node_count {
node := &file.nodes[node_count];
type := read_value(r, Node_Type) or_return;
node := &file.nodes[node_count]
type := read_value(r, Node_Type) or_return
if type > max(Node_Type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type)));
fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type)))
}
err = .Invalid_Data;
return;
err = .Invalid_Data
return
}
node_count += 1;
node_count += 1
node.meta_data = read_meta(r, read_value(r, u32le) or_return) or_return;
node.meta_data = read_meta(r, read_value(r, u32le) or_return) or_return
switch type {
case .Meta_Only:
// Okay
case .Geometry:
g: Node_Geometry;
g: Node_Geometry
g.vertex_count = read_value(r, u32le) or_return;
g.vertex_stack = read_layer_stack(r, g.vertex_count) or_return;
g.edge_corner_count = read_value(r, u32le) or_return;
g.corner_stack = read_layer_stack(r, g.edge_corner_count) or_return;
g.vertex_count = read_value(r, u32le) or_return
g.vertex_stack = read_layer_stack(r, g.vertex_count) or_return
g.edge_corner_count = read_value(r, u32le) or_return
g.corner_stack = read_layer_stack(r, g.edge_corner_count) or_return
if header.version > 2 {
g.edge_stack = read_layer_stack(r, g.edge_corner_count) or_return;
g.edge_stack = read_layer_stack(r, g.edge_corner_count) or_return
}
g.face_count = read_value(r, u32le) or_return;
g.face_stack = read_layer_stack(r, g.face_count) or_return;
g.face_count = read_value(r, u32le) or_return
g.face_stack = read_layer_stack(r, g.face_count) or_return
node.content = g;
node.content = g
case .Image:
img: Node_Image;
img: Node_Image
img.type = read_value(r, Image_Type) or_return;
dimensions := int(img.type);
img.type = read_value(r, Image_Type) or_return
dimensions := int(img.type)
if img.type == .Image_Cube {
dimensions = 2;
dimensions = 2
}
img.resolution = {1, 1, 1};
img.resolution = {1, 1, 1}
for d in 0..<dimensions {
img.resolution[d] = read_value(r, u32le) or_return;
img.resolution[d] = read_value(r, u32le) or_return
}
size := img.resolution[0]*img.resolution[1]*img.resolution[2];
size := img.resolution[0]*img.resolution[1]*img.resolution[2]
if img.type == .Image_Cube {
size *= 6;
size *= 6
}
img.image_stack = read_layer_stack(r, size) or_return;
img.image_stack = read_layer_stack(r, size) or_return
node.content = img;
node.content = img
}
}
return;
return
}
+96 -96
View File
@@ -10,36 +10,36 @@ Write_Error :: enum {
}
write_to_file :: proc(filepath: string, file: File) -> (err: Write_Error) {
required := required_write_size(file);
buf, alloc_err := make([]byte, required);
required := required_write_size(file)
buf, alloc_err := make([]byte, required)
if alloc_err == .Out_Of_Memory {
return .Failed_File_Write;
return .Failed_File_Write
}
defer delete(buf);
defer delete(buf)
write_internal(&Writer{data = buf}, file);
write_internal(&Writer{data = buf}, file)
if !os.write_entire_file(filepath, buf) {
err =.Failed_File_Write;
err =.Failed_File_Write
}
return;
return
}
write :: proc(buf: []byte, file: File) -> (n: int, err: Write_Error) {
required := required_write_size(file);
required := required_write_size(file)
if len(buf) < required {
err = .Buffer_Too_Small;
return;
err = .Buffer_Too_Small
return
}
n = required;
write_internal(&Writer{data = buf}, file);
return;
n = required
write_internal(&Writer{data = buf}, file)
return
}
required_write_size :: proc(file: File) -> (n: int) {
writer := &Writer{dummy_pass = true};
write_internal(writer, file);
n = writer.offset;
return;
writer := &Writer{dummy_pass = true}
write_internal(writer, file)
n = writer.offset
return
}
@@ -48,146 +48,146 @@ Writer :: struct {
data: []byte,
offset: int,
dummy_pass: bool,
};
}
@(private)
write_internal :: proc(w: ^Writer, file: File) {
write_value :: proc(w: ^Writer, value: $T) {
if !w.dummy_pass {
remaining := len(w.data) - w.offset;
assert(size_of(T) <= remaining);
ptr := raw_data(w.data[w.offset:]);
(^T)(ptr)^ = value;
remaining := len(w.data) - w.offset
assert(size_of(T) <= remaining)
ptr := raw_data(w.data[w.offset:])
(^T)(ptr)^ = value
}
w.offset += size_of(T);
w.offset += size_of(T)
}
write_array :: proc(w: ^Writer, array: []$T) {
if !w.dummy_pass {
remaining := len(w.data) - w.offset;
assert(size_of(T)*len(array) <= remaining);
ptr := raw_data(w.data[w.offset:]);
dst := mem.slice_ptr((^T)(ptr), len(array));
copy(dst, array);
remaining := len(w.data) - w.offset
assert(size_of(T)*len(array) <= remaining)
ptr := raw_data(w.data[w.offset:])
dst := mem.slice_ptr((^T)(ptr), len(array))
copy(dst, array)
}
w.offset += size_of(T)*len(array);
w.offset += size_of(T)*len(array)
}
write_string :: proc(w: ^Writer, str: string) {
if !w.dummy_pass {
remaining := len(w.data) - w.offset;
assert(size_of(byte)*len(str) <= remaining);
ptr := raw_data(w.data[w.offset:]);
dst := mem.slice_ptr((^byte)(ptr), len(str));
copy(dst, str);
remaining := len(w.data) - w.offset
assert(size_of(byte)*len(str) <= remaining)
ptr := raw_data(w.data[w.offset:])
dst := mem.slice_ptr((^byte)(ptr), len(str))
copy(dst, str)
}
w.offset += size_of(byte)*len(str);
w.offset += size_of(byte)*len(str)
}
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
for m in meta_data {
name_len := max(len(m.name), 255);
write_value(w, u8(name_len));
write_string(w, m.name[:name_len]);
name_len := max(len(m.name), 255)
write_value(w, u8(name_len))
write_string(w, m.name[:name_len])
meta_data_type: Meta_Value_Type;
length: u32le = 0;
meta_data_type: Meta_Value_Type
length: u32le = 0
switch v in m.value {
case []i64le:
meta_data_type = .Int64;
length = u32le(len(v));
meta_data_type = .Int64
length = u32le(len(v))
case []f64le:
meta_data_type = .Double;
length = u32le(len(v));
meta_data_type = .Double
length = u32le(len(v))
case []Node_Index:
meta_data_type = .Node;
length = u32le(len(v));
meta_data_type = .Node
length = u32le(len(v))
case string:
meta_data_type = .Text;
length = u32le(len(v));
meta_data_type = .Text
length = u32le(len(v))
case []byte:
meta_data_type = .Binary;
length = u32le(len(v));
meta_data_type = .Binary
length = u32le(len(v))
case []Meta:
meta_data_type = .Meta;
length = u32le(len(v));
meta_data_type = .Meta
length = u32le(len(v))
}
write_value(w, meta_data_type);
write_value(w, length);
write_value(w, meta_data_type)
write_value(w, length)
switch v in m.value {
case []i64le: write_array(w, v);
case []f64le: write_array(w, v);
case []Node_Index: write_array(w, v);
case string: write_string(w, v);
case []byte: write_array(w, v);
case []Meta: write_metadata(w, v);
case []i64le: write_array(w, v)
case []f64le: write_array(w, v)
case []Node_Index: write_array(w, v)
case string: write_string(w, v)
case []byte: write_array(w, v)
case []Meta: write_metadata(w, v)
}
}
return;
return
}
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
write_value(w, u32(len(layers)));
write_value(w, u32(len(layers)))
for layer in layers {
name_len := max(len(layer.name), 255);
write_value(w, u8(name_len));
write_string(w, layer .name[:name_len]);
name_len := max(len(layer.name), 255)
write_value(w, u8(name_len))
write_string(w, layer .name[:name_len])
write_value(w, layer.components);
write_value(w, layer.components)
layer_data_type: Layer_Data_Type;
layer_data_type: Layer_Data_Type
switch v in layer.data {
case []u8: layer_data_type = .Uint8;
case []i32le: layer_data_type = .Int32;
case []f32le: layer_data_type = .Float;
case []f64le: layer_data_type = .Double;
case []u8: layer_data_type = .Uint8
case []i32le: layer_data_type = .Int32
case []f32le: layer_data_type = .Float
case []f64le: layer_data_type = .Double
}
write_value(w, layer_data_type);
write_value(w, layer_data_type)
switch v in layer.data {
case []u8: write_array(w, v);
case []i32le: write_array(w, v);
case []f32le: write_array(w, v);
case []f64le: write_array(w, v);
case []u8: write_array(w, v)
case []i32le: write_array(w, v)
case []f32le: write_array(w, v)
case []f64le: write_array(w, v)
}
}
return;
return
}
write_value(w, &Header{
magic_number = MAGIC_NUMBER,
version = LATEST_VERSION,
internal_node_count = u32le(len(file.nodes)),
});
})
for node in file.nodes {
node_type: Node_Type;
node_type: Node_Type
switch content in node.content {
case Node_Geometry: node_type = .Geometry;
case Node_Image: node_type = .Image;
case Node_Geometry: node_type = .Geometry
case Node_Image: node_type = .Image
}
write_value(w, node_type);
write_value(w, node_type)
write_value(w, u32(len(node.meta_data)));
write_metadata(w, node.meta_data);
write_value(w, u32(len(node.meta_data)))
write_metadata(w, node.meta_data)
switch content in node.content {
case Node_Geometry:
write_value(w, content.vertex_count);
write_layer_stack(w, content.vertex_stack);
write_value(w, content.edge_corner_count);
write_layer_stack(w, content.corner_stack);
write_layer_stack(w, content.edge_stack);
write_value(w, content.face_count);
write_layer_stack(w, content.face_stack);
write_value(w, content.vertex_count)
write_layer_stack(w, content.vertex_stack)
write_value(w, content.edge_corner_count)
write_layer_stack(w, content.corner_stack)
write_layer_stack(w, content.edge_stack)
write_value(w, content.face_count)
write_layer_stack(w, content.face_stack)
case Node_Image:
write_value(w, content.type);
dimensions := int(content.type);
write_value(w, content.type)
dimensions := int(content.type)
if content.type == .Image_Cube {
dimensions = 2;
dimensions = 2
}
for d in 0..<dimensions {
write_value(w, content.resolution[d]);
write_value(w, content.resolution[d])
}
write_layer_stack(w, content.image_stack);
write_layer_stack(w, content.image_stack)
}
}
}
+158 -158
View File
@@ -13,305 +13,305 @@ Marshal_Error :: enum {
}
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
b: strings.Builder;
strings.init_builder(&b, allocator);
b: strings.Builder
strings.init_builder(&b, allocator)
err := marshal_arg(&b, v);
err := marshal_arg(&b, v)
if err != .None {
strings.destroy_builder(&b);
return nil, err;
strings.destroy_builder(&b)
return nil, err
}
if len(b.buf) == 0 {
strings.destroy_builder(&b);
return nil, err;
strings.destroy_builder(&b)
return nil, err
}
return b.buf[:], err;
return b.buf[:], err
}
marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
if v == nil {
strings.write_string(b, "null");
return .None;
strings.write_string(b, "null")
return .None
}
ti := runtime.type_info_base(type_info_of(v.id));
a := any{v.data, ti.id};
ti := runtime.type_info_base(type_info_of(v.id))
a := any{v.data, ti.id}
switch info in ti.variant {
case runtime.Type_Info_Named:
unreachable();
unreachable()
case runtime.Type_Info_Integer:
buf: [21]byte;
u: u64;
buf: [21]byte
u: u64
switch i in a {
case i8: u = u64(i);
case i16: u = u64(i);
case i32: u = u64(i);
case i64: u = u64(i);
case int: u = u64(i);
case u8: u = u64(i);
case u16: u = u64(i);
case u32: u = u64(i);
case u64: u = u64(i);
case uint: u = u64(i);
case uintptr: u = u64(i);
case i8: u = u64(i)
case i16: u = u64(i)
case i32: u = u64(i)
case i64: u = u64(i)
case int: u = u64(i)
case u8: u = u64(i)
case u16: u = u64(i)
case u32: u = u64(i)
case u64: u = u64(i)
case uint: u = u64(i)
case uintptr: u = u64(i)
case i16le: u = u64(i);
case i32le: u = u64(i);
case i64le: u = u64(i);
case u16le: u = u64(i);
case u32le: u = u64(i);
case u64le: u = u64(i);
case i16le: u = u64(i)
case i32le: u = u64(i)
case i64le: u = u64(i)
case u16le: u = u64(i)
case u32le: u = u64(i)
case u64le: u = u64(i)
case i16be: u = u64(i);
case i32be: u = u64(i);
case i64be: u = u64(i);
case u16be: u = u64(i);
case u32be: u = u64(i);
case u64be: u = u64(i);
case i16be: u = u64(i)
case i32be: u = u64(i)
case i64be: u = u64(i)
case u16be: u = u64(i)
case u32be: u = u64(i)
case u64be: u = u64(i)
}
s := strconv.append_bits(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil);
strings.write_string(b, s);
s := strconv.append_bits(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
strings.write_string(b, s)
case runtime.Type_Info_Rune:
r := a.(rune);
strings.write_byte(b, '"');
strings.write_escaped_rune(b, r, '"', true);
strings.write_byte(b, '"');
r := a.(rune)
strings.write_byte(b, '"')
strings.write_escaped_rune(b, r, '"', true)
strings.write_byte(b, '"')
case runtime.Type_Info_Float:
val: f64;
val: f64
switch f in a {
case f16: val = f64(f);
case f32: val = f64(f);
case f64: val = f64(f);
case f16: val = f64(f)
case f32: val = f64(f)
case f64: val = f64(f)
}
buf: [386]byte;
buf: [386]byte
str := strconv.append_float(buf[1:], val, 'f', 2*ti.size, 8*ti.size);
s := buf[:len(str)+1];
str := strconv.append_float(buf[1:], val, 'f', 2*ti.size, 8*ti.size)
s := buf[:len(str)+1]
if s[1] == '+' || s[1] == '-' {
s = s[1:];
s = s[1:]
} else {
s[0] = '+';
s[0] = '+'
}
if s[0] == '+' {
s = s[1:];
s = s[1:]
}
strings.write_string(b, string(s));
strings.write_string(b, string(s))
case runtime.Type_Info_Complex:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Quaternion:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_String:
switch s in a {
case string: strings.write_quoted_string(b, s);
case cstring: strings.write_quoted_string(b, string(s));
case string: strings.write_quoted_string(b, s)
case cstring: strings.write_quoted_string(b, string(s))
}
case runtime.Type_Info_Boolean:
val: bool;
val: bool
switch b in a {
case bool: val = bool(b);
case b8: val = bool(b);
case b16: val = bool(b);
case b32: val = bool(b);
case b64: val = bool(b);
case bool: val = bool(b)
case b8: val = bool(b)
case b16: val = bool(b)
case b32: val = bool(b)
case b64: val = bool(b)
}
strings.write_string(b, val ? "true" : "false");
strings.write_string(b, val ? "true" : "false")
case runtime.Type_Info_Any:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Type_Id:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Pointer:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Multi_Pointer:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Procedure:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Tuple:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Enumerated_Array:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Simd_Vector:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Relative_Pointer:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Relative_Slice:
return .Unsupported_Type;
return .Unsupported_Type
case runtime.Type_Info_Array:
strings.write_byte(b, '[');
strings.write_byte(b, '[')
for i in 0..<info.count {
if i > 0 { strings.write_string(b, ", "); }
if i > 0 { strings.write_string(b, ", ") }
data := uintptr(v.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_arg(b, any{rawptr(data), info.elem.id})
}
strings.write_byte(b, ']');
strings.write_byte(b, ']')
case runtime.Type_Info_Dynamic_Array:
strings.write_byte(b, '[');
array := cast(^mem.Raw_Dynamic_Array)v.data;
strings.write_byte(b, '[')
array := cast(^mem.Raw_Dynamic_Array)v.data
for i in 0..<array.len {
if i > 0 { strings.write_string(b, ", "); }
if i > 0 { strings.write_string(b, ", ") }
data := uintptr(array.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
data := uintptr(array.data) + uintptr(i*info.elem_size)
marshal_arg(b, any{rawptr(data), info.elem.id})
}
strings.write_byte(b, ']');
strings.write_byte(b, ']')
case runtime.Type_Info_Slice:
strings.write_byte(b, '[');
slice := cast(^mem.Raw_Slice)v.data;
strings.write_byte(b, '[')
slice := cast(^mem.Raw_Slice)v.data
for i in 0..<slice.len {
if i > 0 { strings.write_string(b, ", "); }
if i > 0 { strings.write_string(b, ", ") }
data := uintptr(slice.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
data := uintptr(slice.data) + uintptr(i*info.elem_size)
marshal_arg(b, any{rawptr(data), info.elem.id})
}
strings.write_byte(b, ']');
strings.write_byte(b, ']')
case runtime.Type_Info_Map:
m := (^mem.Raw_Map)(v.data);
m := (^mem.Raw_Map)(v.data)
strings.write_byte(b, '{');
strings.write_byte(b, '{')
if m != nil {
if info.generated_struct == nil {
return .Unsupported_Type;
return .Unsupported_Type
}
entries := &m.entries;
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct);
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array);
entry_type := ed.elem.variant.(runtime.Type_Info_Struct);
entry_size := ed.elem_size;
entries := &m.entries
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
entry_size := ed.elem_size
for i in 0..<entries.len {
if i > 0 { strings.write_string(b, ", "); }
if i > 0 { strings.write_string(b, ", ") }
data := uintptr(entries.data) + uintptr(i*entry_size);
key := rawptr(data + entry_type.offsets[2]);
value := rawptr(data + entry_type.offsets[3]);
data := uintptr(entries.data) + uintptr(i*entry_size)
key := rawptr(data + entry_type.offsets[2])
value := rawptr(data + entry_type.offsets[3])
marshal_arg(b, any{key, info.key.id});
strings.write_string(b, ": ");
marshal_arg(b, any{value, info.value.id});
marshal_arg(b, any{key, info.key.id})
strings.write_string(b, ": ")
marshal_arg(b, any{value, info.value.id})
}
}
strings.write_byte(b, '}');
strings.write_byte(b, '}')
case runtime.Type_Info_Struct:
strings.write_byte(b, '{');
strings.write_byte(b, '{')
for name, i in info.names {
if i > 0 { strings.write_string(b, ", "); }
strings.write_quoted_string(b, name);
strings.write_string(b, ": ");
if i > 0 { strings.write_string(b, ", ") }
strings.write_quoted_string(b, name)
strings.write_string(b, ": ")
id := info.types[i].id;
data := rawptr(uintptr(v.data) + info.offsets[i]);
marshal_arg(b, any{data, id});
id := info.types[i].id
data := rawptr(uintptr(v.data) + info.offsets[i])
marshal_arg(b, any{data, id})
}
strings.write_byte(b, '}');
strings.write_byte(b, '}')
case runtime.Type_Info_Union:
tag_ptr := uintptr(v.data) + info.tag_offset;
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
tag_ptr := uintptr(v.data) + info.tag_offset
tag_any := any{rawptr(tag_ptr), info.tag_type.id}
tag: i64 = -1;
tag: i64 = -1
switch i in tag_any {
case u8: tag = i64(i);
case i8: tag = i64(i);
case u16: tag = i64(i);
case i16: tag = i64(i);
case u32: tag = i64(i);
case i32: tag = i64(i);
case u64: tag = i64(i);
case i64: tag = i64(i);
case: panic("Invalid union tag type");
case u8: tag = i64(i)
case i8: tag = i64(i)
case u16: tag = i64(i)
case i16: tag = i64(i)
case u32: tag = i64(i)
case i32: tag = i64(i)
case u64: tag = i64(i)
case i64: tag = i64(i)
case: panic("Invalid union tag type")
}
if v.data == nil || tag == 0 {
strings.write_string(b, "null");
strings.write_string(b, "null")
} else {
id := info.variants[tag-1].id;
marshal_arg(b, any{v.data, id});
id := info.variants[tag-1].id
marshal_arg(b, any{v.data, id})
}
case runtime.Type_Info_Enum:
return marshal_arg(b, any{v.data, info.base.id});
return marshal_arg(b, any{v.data, info.base.id})
case runtime.Type_Info_Bit_Set:
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
if ti == nil {
return false;
return false
}
t := runtime.type_info_base(ti);
t := runtime.type_info_base(ti)
#partial switch info in t.variant {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false;
case .Little: return ODIN_ENDIAN != "little";
case .Big: return ODIN_ENDIAN != "big";
case .Platform: return false
case .Little: return ODIN_ENDIAN != "little"
case .Big: return ODIN_ENDIAN != "big"
}
}
return false;
return false
}
bit_data: u64;
bit_size := u64(8*ti.size);
bit_data: u64
bit_size := u64(8*ti.size)
do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying);
do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying)
switch bit_size {
case 0: bit_data = 0;
case 0: bit_data = 0
case 8:
x := (^u8)(v.data)^;
bit_data = u64(x);
x := (^u8)(v.data)^
bit_data = u64(x)
case 16:
x := (^u16)(v.data)^;
x := (^u16)(v.data)^
if do_byte_swap {
x = bits.byte_swap(x);
x = bits.byte_swap(x)
}
bit_data = u64(x);
bit_data = u64(x)
case 32:
x := (^u32)(v.data)^;
x := (^u32)(v.data)^
if do_byte_swap {
x = bits.byte_swap(x);
x = bits.byte_swap(x)
}
bit_data = u64(x);
bit_data = u64(x)
case 64:
x := (^u64)(v.data)^;
x := (^u64)(v.data)^
if do_byte_swap {
x = bits.byte_swap(x);
x = bits.byte_swap(x)
}
bit_data = u64(x);
case: panic("unknown bit_size size");
bit_data = u64(x)
case: panic("unknown bit_size size")
}
strings.write_u64(b, bit_data);
strings.write_u64(b, bit_data)
return .Unsupported_Type;
return .Unsupported_Type
}
return .None;
return .None
}
+203 -203
View File
@@ -15,235 +15,235 @@ Parser :: struct {
}
make_parser :: proc(data: []byte, spec := Specification.JSON, parse_integers := false, allocator := context.allocator) -> Parser {
p: Parser;
p.tok = make_tokenizer(data, spec, parse_integers);
p.spec = spec;
p.allocator = allocator;
assert(p.allocator.procedure != nil);
advance_token(&p);
return p;
p: Parser
p.tok = make_tokenizer(data, spec, parse_integers)
p.spec = spec
p.allocator = allocator
assert(p.allocator.procedure != nil)
advance_token(&p)
return p
}
parse :: proc(data: []byte, spec := Specification.JSON, parse_integers := false, allocator := context.allocator) -> (Value, Error) {
context.allocator = allocator;
p := make_parser(data, spec, parse_integers, allocator);
context.allocator = allocator
p := make_parser(data, spec, parse_integers, allocator)
if p.spec == Specification.JSON5 {
return parse_value(&p);
return parse_value(&p)
}
return parse_object(&p);
return parse_object(&p)
}
token_end_pos :: proc(tok: Token) -> Pos {
end := tok.pos;
end.offset += len(tok.text);
return end;
end := tok.pos
end.offset += len(tok.text)
return end
}
advance_token :: proc(p: ^Parser) -> (Token, Error) {
err: Error;
p.prev_token = p.curr_token;
p.curr_token, err = get_token(&p.tok);
return p.prev_token, err;
err: Error
p.prev_token = p.curr_token
p.curr_token, err = get_token(&p.tok)
return p.prev_token, err
}
allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
if p.curr_token.kind == kind {
advance_token(p);
return true;
advance_token(p)
return true
}
return false;
return false
}
expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
prev := p.curr_token;
advance_token(p);
prev := p.curr_token
advance_token(p)
if prev.kind == kind {
return .None;
return .None
}
return .Unexpected_Token;
return .Unexpected_Token
}
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
token := p.curr_token;
token := p.curr_token
#partial switch token.kind {
case .Null:
value = Null{};
advance_token(p);
return;
value = Null{}
advance_token(p)
return
case .False:
value = Boolean(false);
advance_token(p);
return;
value = Boolean(false)
advance_token(p)
return
case .True:
value = Boolean(true);
advance_token(p);
return;
value = Boolean(true)
advance_token(p)
return
case .Integer:
i, _ := strconv.parse_i64(token.text);
value = Integer(i);
advance_token(p);
return;
i, _ := strconv.parse_i64(token.text)
value = Integer(i)
advance_token(p)
return
case .Float:
f, _ := strconv.parse_f64(token.text);
value = Float(f);
advance_token(p);
return;
f, _ := strconv.parse_f64(token.text)
value = Float(f)
advance_token(p)
return
case .String:
value = String(unquote_string(token, p.spec, p.allocator));
advance_token(p);
return;
value = String(unquote_string(token, p.spec, p.allocator))
advance_token(p)
return
case .Open_Brace:
return parse_object(p);
return parse_object(p)
case .Open_Bracket:
return parse_array(p);
return parse_array(p)
case:
if p.spec == Specification.JSON5 {
#partial switch token.kind {
case .Infinity:
inf: u64 = 0x7ff0000000000000;
inf: u64 = 0x7ff0000000000000
if token.text[0] == '-' {
inf = 0xfff0000000000000;
inf = 0xfff0000000000000
}
value = transmute(f64)inf;
advance_token(p);
return;
value = transmute(f64)inf
advance_token(p)
return
case .NaN:
nan: u64 = 0x7ff7ffffffffffff;
nan: u64 = 0x7ff7ffffffffffff
if token.text[0] == '-' {
nan = 0xfff7ffffffffffff;
nan = 0xfff7ffffffffffff
}
value = transmute(f64)nan;
advance_token(p);
return;
value = transmute(f64)nan
advance_token(p)
return
}
}
}
err = .Unexpected_Token;
advance_token(p);
return;
err = .Unexpected_Token
advance_token(p)
return
}
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
expect_token(p, .Open_Bracket) or_return;
expect_token(p, .Open_Bracket) or_return
array: Array;
array.allocator = p.allocator;
array: Array
array.allocator = p.allocator
defer if err != .None {
for elem in array {
destroy_value(elem);
destroy_value(elem)
}
delete(array);
delete(array)
}
for p.curr_token.kind != .Close_Bracket {
elem := parse_value(p) or_return;
append(&array, elem);
elem := parse_value(p) or_return
append(&array, elem)
// Disallow trailing commas for the time being
if allow_token(p, .Comma) {
continue;
continue
} else {
break;
break
}
}
expect_token(p, .Close_Bracket) or_return;
value = array;
return;
expect_token(p, .Close_Bracket) or_return
value = array
return
}
clone_string :: proc(s: string, allocator: mem.Allocator) -> string {
n := len(s);
b := make([]byte, n+1, allocator);
copy(b, s);
b[n] = 0;
return string(b[:n]);
n := len(s)
b := make([]byte, n+1, allocator)
copy(b, s)
b[n] = 0
return string(b[:n])
}
parse_object_key :: proc(p: ^Parser) -> (key: string, err: Error) {
tok := p.curr_token;
tok := p.curr_token
if p.spec == Specification.JSON5 {
if tok.kind == .String {
expect_token(p, .String);
key = unquote_string(tok, p.spec, p.allocator);
return;
expect_token(p, .String)
key = unquote_string(tok, p.spec, p.allocator)
return
} else if tok.kind == .Ident {
expect_token(p, .Ident);
key = clone_string(tok.text, p.allocator);
return;
expect_token(p, .Ident)
key = clone_string(tok.text, p.allocator)
return
}
}
if tok_err := expect_token(p, .String); tok_err != .None {
err = .Expected_String_For_Object_Key;
return;
err = .Expected_String_For_Object_Key
return
}
key = unquote_string(tok, p.spec, p.allocator);
return;
key = unquote_string(tok, p.spec, p.allocator)
return
}
parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
expect_token(p, .Open_Brace) or_return;
expect_token(p, .Open_Brace) or_return
obj: Object;
obj.allocator = p.allocator;
obj: Object
obj.allocator = p.allocator
defer if err != .None {
for key, elem in obj {
delete(key, p.allocator);
destroy_value(elem);
delete(key, p.allocator)
destroy_value(elem)
}
delete(obj);
delete(obj)
}
for p.curr_token.kind != .Close_Brace {
key: string;
key, err = parse_object_key(p);
key: string
key, err = parse_object_key(p)
if err != .None {
delete(key, p.allocator);
return;
delete(key, p.allocator)
return
}
if colon_err := expect_token(p, .Colon); colon_err != .None {
err = .Expected_Colon_After_Key;
return;
err = .Expected_Colon_After_Key
return
}
elem := parse_value(p) or_return;
elem := parse_value(p) or_return
if key in obj {
err = .Duplicate_Object_Key;
delete(key, p.allocator);
return;
err = .Duplicate_Object_Key
delete(key, p.allocator)
return
}
obj[key] = elem;
obj[key] = elem
if p.spec == Specification.JSON5 {
// Allow trailing commas
if allow_token(p, .Comma) {
continue;
continue
}
} else {
// Disallow trailing commas
if allow_token(p, .Comma) {
continue;
continue
} else {
break;
break
}
}
}
expect_token(p, .Close_Brace) or_return;
value = obj;
return;
expect_token(p, .Close_Brace) or_return
value = obj
return
}
@@ -251,177 +251,177 @@ parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator) -> string {
get_u2_rune :: proc(s: string) -> rune {
if len(s) < 4 || s[0] != '\\' || s[1] != 'x' {
return -1;
return -1
}
r: rune;
r: rune
for c in s[2:4] {
x: rune;
x: rune
switch c {
case '0'..='9': x = c - '0';
case 'a'..='f': x = c - 'a' + 10;
case 'A'..='F': x = c - 'A' + 10;
case: return -1;
case '0'..='9': x = c - '0'
case 'a'..='f': x = c - 'a' + 10
case 'A'..='F': x = c - 'A' + 10
case: return -1
}
r = r*16 + x;
r = r*16 + x
}
return r;
return r
}
get_u4_rune :: proc(s: string) -> rune {
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
return -1;
return -1
}
r: rune;
r: rune
for c in s[2:6] {
x: rune;
x: rune
switch c {
case '0'..='9': x = c - '0';
case 'a'..='f': x = c - 'a' + 10;
case 'A'..='F': x = c - 'A' + 10;
case: return -1;
case '0'..='9': x = c - '0'
case 'a'..='f': x = c - 'a' + 10
case 'A'..='F': x = c - 'A' + 10
case: return -1
}
r = r*16 + x;
r = r*16 + x
}
return r;
return r
}
if token.kind != .String {
return "";
return ""
}
s := token.text;
s := token.text
if len(s) <= 2 {
return "";
return ""
}
quote := s[0];
quote := s[0]
if s[0] != s[len(s)-1] {
// Invalid string
return "";
return ""
}
s = s[1:len(s)-1];
s = s[1:len(s)-1]
i := 0;
i := 0
for i < len(s) {
c := s[i];
c := s[i]
if c == '\\' || c == quote || c < ' ' {
break;
break
}
if c < utf8.RUNE_SELF {
i += 1;
continue;
i += 1
continue
}
r, w := utf8.decode_rune_in_string(s);
r, w := utf8.decode_rune_in_string(s)
if r == utf8.RUNE_ERROR && w == 1 {
break;
break
}
i += w;
i += w
}
if i == len(s) {
return clone_string(s, allocator);
return clone_string(s, allocator)
}
b := make([]byte, len(s) + 2*utf8.UTF_MAX, allocator);
w := copy(b, s[0:i]);
b := make([]byte, len(s) + 2*utf8.UTF_MAX, allocator)
w := copy(b, s[0:i])
loop: for i < len(s) {
c := s[i];
c := s[i]
switch {
case c == '\\':
i += 1;
i += 1
if i >= len(s) {
break loop;
break loop
}
switch s[i] {
case: break loop;
case: break loop
case '"', '\'', '\\', '/':
b[w] = s[i];
i += 1;
w += 1;
b[w] = s[i]
i += 1
w += 1
case 'b':
b[w] = '\b';
i += 1;
w += 1;
b[w] = '\b'
i += 1
w += 1
case 'f':
b[w] = '\f';
i += 1;
w += 1;
b[w] = '\f'
i += 1
w += 1
case 'r':
b[w] = '\r';
i += 1;
w += 1;
b[w] = '\r'
i += 1
w += 1
case 't':
b[w] = '\t';
i += 1;
w += 1;
b[w] = '\t'
i += 1
w += 1
case 'n':
b[w] = '\n';
i += 1;
w += 1;
b[w] = '\n'
i += 1
w += 1
case 'u':
i -= 1; // Include the \u in the check for sanity sake
r := get_u4_rune(s[i:]);
i -= 1 // Include the \u in the check for sanity sake
r := get_u4_rune(s[i:])
if r < 0 {
break loop;
break loop
}
i += 6;
i += 6
buf, buf_width := utf8.encode_rune(r);
copy(b[w:], buf[:buf_width]);
w += buf_width;
buf, buf_width := utf8.encode_rune(r)
copy(b[w:], buf[:buf_width])
w += buf_width
case '0':
if spec == Specification.JSON5 {
b[w] = '\x00';
i += 1;
w += 1;
b[w] = '\x00'
i += 1
w += 1
} else {
break loop;
break loop
}
case 'v':
if spec == Specification.JSON5 {
b[w] = '\v';
i += 1;
w += 1;
b[w] = '\v'
i += 1
w += 1
} else {
break loop;
break loop
}
case 'x':
if spec == Specification.JSON5 {
i -= 1; // Include the \x in the check for sanity sake
r := get_u2_rune(s[i:]);
i -= 1 // Include the \x in the check for sanity sake
r := get_u2_rune(s[i:])
if r < 0 {
break loop;
break loop
}
i += 4;
i += 4
buf, buf_width := utf8.encode_rune(r);
copy(b[w:], buf[:buf_width]);
w += buf_width;
buf, buf_width := utf8.encode_rune(r)
copy(b[w:], buf[:buf_width])
w += buf_width
} else {
break loop;
break loop
}
}
case c == quote, c < ' ':
break loop;
break loop
case c < utf8.RUNE_SELF:
b[w] = c;
i += 1;
w += 1;
b[w] = c
i += 1
w += 1
case:
r, width := utf8.decode_rune_in_string(s[i:]);
i += width;
r, width := utf8.decode_rune_in_string(s[i:])
i += width
buf, buf_width := utf8.encode_rune(r);
assert(buf_width <= width);
copy(b[w:], buf[:buf_width]);
w += buf_width;
buf, buf_width := utf8.encode_rune(r)
assert(buf_width <= width)
copy(b[w:], buf[:buf_width])
w += buf_width
}
}
return string(b[:w]);
return string(b[:w])
}
+152 -152
View File
@@ -54,22 +54,22 @@ Tokenizer :: struct {
make_tokenizer :: proc(data: []byte, spec := Specification.JSON, parse_integers := false) -> Tokenizer {
t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers};
next_rune(&t);
t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers}
next_rune(&t)
if t.r == utf8.RUNE_BOM {
next_rune(&t);
next_rune(&t)
}
return t;
return t
}
next_rune :: proc(t: ^Tokenizer) -> rune #no_bounds_check {
if t.offset >= len(t.data) {
return utf8.RUNE_EOF;
return utf8.RUNE_EOF
}
t.offset += t.w;
t.r, t.w = utf8.decode_rune(t.data[t.offset:]);
t.pos.column = t.offset - t.curr_line_offset;
return t.r;
t.offset += t.w
t.r, t.w = utf8.decode_rune(t.data[t.offset:])
t.pos.column = t.offset - t.curr_line_offset
return t.r
}
@@ -79,19 +79,19 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
if '0' <= t.r && t.r <= '9' {
// Okay
} else {
return;
return
}
next_rune(t);
next_rune(t)
}
}
skip_hex_digits :: proc(t: ^Tokenizer) {
for t.offset < len(t.data) {
next_rune(t);
next_rune(t)
switch t.r {
case '0'..='9', 'a'..='f', 'A'..='F':
// Okay
case:
return;
return
}
}
}
@@ -99,56 +99,56 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
scan_espace :: proc(t: ^Tokenizer) -> bool {
switch t.r {
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
next_rune(t);
return true;
next_rune(t)
return true
case 'u':
// Expect 4 hexadecimal digits
for i := 0; i < 4; i += 1 {
r := next_rune(t);
r := next_rune(t)
switch r {
case '0'..='9', 'a'..='f', 'A'..='F':
// Okay
case:
return false;
return false
}
}
return true;
return true
case:
// Ignore the next rune regardless
next_rune(t);
next_rune(t)
}
return false;
return false
}
skip_whitespace :: proc(t: ^Tokenizer) -> rune {
loop: for t.offset < len(t.data) {
switch t.r {
case ' ', '\t', '\v', '\f', '\r':
next_rune(t);
next_rune(t)
case '\n':
t.line += 1;
t.curr_line_offset = t.offset;
t.pos.column = 1;
next_rune(t);
t.line += 1
t.curr_line_offset = t.offset
t.pos.column = 1
next_rune(t)
case:
if t.spec == .JSON5 {
switch t.r {
case 0x2028, 0x2029, 0xFEFF:
next_rune(t);
continue loop;
next_rune(t)
continue loop
}
}
break loop;
break loop
}
}
return t.r;
return t.r
}
skip_to_next_line :: proc(t: ^Tokenizer) {
for t.offset < len(t.data) {
r := next_rune(t);
r := next_rune(t)
if r == '\n' {
return;
return
}
}
}
@@ -157,53 +157,53 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
for t.offset < len(t.data) {
switch next_rune(t) {
case 'A'..='Z', 'a'..='z', '0'..='9', '_':
continue;
continue
}
return;
return
}
}
skip_whitespace(t);
skip_whitespace(t)
token.pos = t.pos;
token.pos = t.pos
token.kind = .Invalid;
token.kind = .Invalid
curr_rune := t.r;
next_rune(t);
curr_rune := t.r
next_rune(t)
block: switch curr_rune {
case utf8.RUNE_ERROR:
err = .Illegal_Character;
err = .Illegal_Character
case utf8.RUNE_EOF, '\x00':
token.kind = .EOF;
err = .EOF;
token.kind = .EOF
err = .EOF
case 'A'..='Z', 'a'..='z', '_':
token.kind = .Ident;
token.kind = .Ident
skip_alphanum(t);
skip_alphanum(t)
switch str := string(t.data[token.offset:t.offset]); str {
case "null": token.kind = .Null;
case "false": token.kind = .False;
case "true": token.kind = .True;
case "null": token.kind = .Null
case "false": token.kind = .False
case "true": token.kind = .True
case:
if t.spec == .JSON5 {
switch str {
case "Infinity": token.kind = .Infinity;
case "NaN": token.kind = .NaN;
case "Infinity": token.kind = .Infinity
case "NaN": token.kind = .NaN
}
}
}
case '+':
err = .Illegal_Character;
err = .Illegal_Character
if t.spec != .JSON5 {
break;
break
}
fallthrough;
fallthrough
case '-':
switch t.r {
@@ -211,281 +211,281 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
// Okay
case:
// Illegal use of +/-
err = .Illegal_Character;
err = .Illegal_Character
if t.spec == .JSON5 {
if t.r == 'I' || t.r == 'N' {
skip_alphanum(t);
skip_alphanum(t)
}
switch string(t.data[token.offset:t.offset]) {
case "-Infinity": token.kind = .Infinity;
case "-NaN": token.kind = .NaN;
case "-Infinity": token.kind = .Infinity
case "-NaN": token.kind = .NaN
}
}
break block;
break block
}
fallthrough;
fallthrough
case '0'..='9':
token.kind = t.parse_integers ? .Integer : .Float;
token.kind = t.parse_integers ? .Integer : .Float
if t.spec == .JSON5 { // Hexadecimal Numbers
if curr_rune == '0' && (t.r == 'x' || t.r == 'X') {
next_rune(t);
skip_hex_digits(t);
break;
next_rune(t)
skip_hex_digits(t)
break
}
}
skip_digits(t);
skip_digits(t)
if t.r == '.' {
token.kind = .Float;
next_rune(t);
skip_digits(t);
token.kind = .Float
next_rune(t)
skip_digits(t)
}
if t.r == 'e' || t.r == 'E' {
switch r := next_rune(t); r {
case '+', '-':
next_rune(t);
next_rune(t)
}
skip_digits(t);
skip_digits(t)
}
str := string(t.data[token.offset:t.offset]);
str := string(t.data[token.offset:t.offset])
if !is_valid_number(str, t.spec) {
err = .Invalid_Number;
err = .Invalid_Number
}
case '.':
err = .Illegal_Character;
err = .Illegal_Character
if t.spec == .JSON5 { // Allow leading decimal point
skip_digits(t);
skip_digits(t)
if t.r == 'e' || t.r == 'E' {
switch r := next_rune(t); r {
case '+', '-':
next_rune(t);
next_rune(t)
}
skip_digits(t);
skip_digits(t)
}
str := string(t.data[token.offset:t.offset]);
str := string(t.data[token.offset:t.offset])
if !is_valid_number(str, t.spec) {
err = .Invalid_Number;
err = .Invalid_Number
}
}
case '\'':
err = .Illegal_Character;
err = .Illegal_Character
if t.spec != .JSON5 {
break;
break
}
fallthrough;
fallthrough
case '"':
token.kind = .String;
quote := curr_rune;
token.kind = .String
quote := curr_rune
for t.offset < len(t.data) {
r := t.r;
r := t.r
if r == '\n' || r < 0 {
err = .String_Not_Terminated;
break;
err = .String_Not_Terminated
break
}
next_rune(t);
next_rune(t)
if r == quote {
break;
break
}
if r == '\\' {
scan_espace(t);
scan_espace(t)
}
}
str := string(t.data[token.offset : t.offset]);
str := string(t.data[token.offset : t.offset])
if !is_valid_string_literal(str, t.spec) {
err = .Invalid_String;
err = .Invalid_String
}
case ',': token.kind = .Comma;
case ':': token.kind = .Colon;
case '{': token.kind = .Open_Brace;
case '}': token.kind = .Close_Brace;
case '[': token.kind = .Open_Bracket;
case ']': token.kind = .Close_Bracket;
case ',': token.kind = .Comma
case ':': token.kind = .Colon
case '{': token.kind = .Open_Brace
case '}': token.kind = .Close_Brace
case '[': token.kind = .Open_Bracket
case ']': token.kind = .Close_Bracket
case '/':
err = .Illegal_Character;
err = .Illegal_Character
if t.spec == .JSON5 {
switch t.r {
case '/':
// Single-line comments
skip_to_next_line(t);
return get_token(t);
skip_to_next_line(t)
return get_token(t)
case '*':
// None-nested multi-line comments
for t.offset < len(t.data) {
next_rune(t);
next_rune(t)
if t.r == '*' {
next_rune(t);
next_rune(t)
if t.r == '/' {
next_rune(t);
return get_token(t);
next_rune(t)
return get_token(t)
}
}
}
err = .EOF;
err = .EOF
}
}
case: err = .Illegal_Character;
case: err = .Illegal_Character
}
token.text = string(t.data[token.offset : t.offset]);
token.text = string(t.data[token.offset : t.offset])
return;
return
}
is_valid_number :: proc(str: string, spec: Specification) -> bool {
s := str;
s := str
if s == "" {
return false;
return false
}
if s[0] == '-' {
s = s[1:];
s = s[1:]
if s == "" {
return false;
return false
}
} else if spec == .JSON5 {
if s[0] == '+' { // Allow positive sign
s = s[1:];
s = s[1:]
if s == "" {
return false;
return false
}
}
}
switch s[0] {
case '0':
s = s[1:];
s = s[1:]
case '1'..='9':
s = s[1:];
s = s[1:]
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
s = s[1:];
s = s[1:]
}
case '.':
if spec == .JSON5 { // Allow leading decimal point
s = s[1:];
s = s[1:]
} else {
return false;
return false
}
case:
return false;
return false
}
if spec == .JSON5 {
if len(s) == 1 && s[0] == '.' { // Allow trailing decimal point
return true;
return true
}
}
if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
s = s[2:];
s = s[2:]
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
s = s[1:];
s = s[1:]
}
}
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
s = s[1:];
s = s[1:]
switch s[0] {
case '+', '-':
s = s[1:];
s = s[1:]
if s == "" {
return false;
return false
}
}
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
s = s[1:];
s = s[1:]
}
}
// The string should be empty now to be valid
return s == "";
return s == ""
}
is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
s := str;
s := str
if len(s) < 2 {
return false;
return false
}
quote := s[0];
quote := s[0]
if s[0] != s[len(s)-1] {
return false;
return false
}
if s[0] != '"' || s[len(s)-1] != '"' {
if spec == .JSON5 {
if s[0] != '\'' || s[len(s)-1] != '\'' {
return false;
return false
}
} else {
return false;
return false
}
}
s = s[1 : len(s)-1];
s = s[1 : len(s)-1]
i := 0;
i := 0
for i < len(s) {
c := s[i];
c := s[i]
switch {
case c == '\\':
i += 1;
i += 1
if i >= len(s) {
return false;
return false
}
switch s[i] {
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
i += 1;
i += 1
case 'u':
if i >= len(s) {
return false;
return false
}
hex := s[i+1:];
hex := s[i+1:]
if len(hex) < 4 {
return false;
return false
}
hex = hex[:4];
i += 5;
hex = hex[:4]
i += 5
for j := 0; j < 4; j += 1 {
c2 := hex[j];
c2 := hex[j]
switch c2 {
case '0'..='9', 'a'..='z', 'A'..='Z':
// Okay
case:
return false;
return false
}
}
case: return false;
case: return false
}
case c == quote, c < ' ':
return false;
return false
case c < utf8.RUNE_SELF:
i += 1;
i += 1
case:
r, width := utf8.decode_rune_in_string(s[i:]);
r, width := utf8.decode_rune_in_string(s[i:])
if r == utf8.RUNE_ERROR && width == 1 {
return false;
return false
}
i += width;
i += width
}
}
if i == len(s) {
return true;
return true
}
return true;
return true
}
+13 -13
View File
@@ -6,13 +6,13 @@ Specification :: enum {
// MJSON, // http://bitsquid.blogspot.com/2009/09/json-configuration-data.html
}
Null :: distinct rawptr;
Integer :: i64;
Float :: f64;
Boolean :: bool;
String :: string;
Array :: distinct [dynamic]Value;
Object :: distinct map[string]Value;
Null :: distinct rawptr
Integer :: i64
Float :: f64
Boolean :: bool
String :: string
Array :: distinct [dynamic]Value
Object :: distinct map[string]Value
Value :: union {
Null,
@@ -50,17 +50,17 @@ destroy_value :: proc(value: Value) {
#partial switch v in value {
case Object:
for key, elem in v {
delete(key);
destroy_value(elem);
delete(key)
destroy_value(elem)
}
delete(v);
delete(v)
case Array:
for elem in v {
destroy_value(elem);
destroy_value(elem)
}
delete(v);
delete(v)
case String:
delete(v);
delete(v)
}
}
+37 -37
View File
@@ -4,119 +4,119 @@ import "core:mem"
// NOTE(bill): is_valid will not check for duplicate keys
is_valid :: proc(data: []byte, spec := Specification.JSON, parse_integers := false) -> bool {
p := make_parser(data, spec, parse_integers, mem.nil_allocator());
p := make_parser(data, spec, parse_integers, mem.nil_allocator())
if p.spec == Specification.JSON5 {
return validate_value(&p);
return validate_value(&p)
}
return validate_object(&p);
return validate_object(&p)
}
validate_object_key :: proc(p: ^Parser) -> bool {
tok := p.curr_token;
tok := p.curr_token
if p.spec == Specification.JSON5 {
if tok.kind == .String {
expect_token(p, .String);
return true;
expect_token(p, .String)
return true
} else if tok.kind == .Ident {
expect_token(p, .Ident);
return true;
expect_token(p, .Ident)
return true
}
}
err := expect_token(p, .String);
return err == Error.None;
err := expect_token(p, .String)
return err == Error.None
}
validate_object :: proc(p: ^Parser) -> bool {
if err := expect_token(p, .Open_Brace); err != Error.None {
return false;
return false
}
for p.curr_token.kind != .Close_Brace {
if !validate_object_key(p) {
return false;
return false
}
if colon_err := expect_token(p, .Colon); colon_err != Error.None {
return false;
return false
}
if !validate_value(p) {
return false;
return false
}
if p.spec == Specification.JSON5 {
// Allow trailing commas
if allow_token(p, .Comma) {
continue;
continue
}
} else {
// Disallow trailing commas
if allow_token(p, .Comma) {
continue;
continue
} else {
break;
break
}
}
}
if err := expect_token(p, .Close_Brace); err != Error.None {
return false;
return false
}
return true;
return true
}
validate_array :: proc(p: ^Parser) -> bool {
if err := expect_token(p, .Open_Bracket); err != Error.None {
return false;
return false
}
for p.curr_token.kind != .Close_Bracket {
if !validate_value(p) {
return false;
return false
}
// Disallow trailing commas for the time being
if allow_token(p, .Comma) {
continue;
continue
} else {
break;
break
}
}
if err := expect_token(p, .Close_Bracket); err != Error.None {
return false;
return false
}
return true;
return true
}
validate_value :: proc(p: ^Parser) -> bool {
token := p.curr_token;
token := p.curr_token
#partial switch token.kind {
case .Null, .False, .True:
advance_token(p);
return true;
advance_token(p)
return true
case .Integer, .Float:
advance_token(p);
return true;
advance_token(p)
return true
case .String:
advance_token(p);
return is_valid_string_literal(token.text, p.spec);
advance_token(p)
return is_valid_string_literal(token.text, p.spec)
case .Open_Brace:
return validate_object(p);
return validate_object(p)
case .Open_Bracket:
return validate_array(p);
return validate_array(p)
case:
if p.spec == Specification.JSON5 {
#partial switch token.kind {
case .Infinity, .NaN:
advance_token(p);
return true;
advance_token(p)
return true
}
}
}
return false;
return false
}
+953 -953
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -3,11 +3,11 @@ package hash
@(optimization_mode="speed")
crc64 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
result := ~u64(seed);
result := ~u64(seed)
#no_bounds_check for b in data {
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff]
}
return ~result;
return ~result
}
@private _crc64_table := [256]u64{
@@ -75,4 +75,4 @@ crc64 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9,
0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8,
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
};
}
+17 -17
View File
@@ -4,20 +4,20 @@ import "core:intrinsics"
@(optimization_mode="speed")
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
crc := ~seed;
buffer := raw_data(data);
length := len(data);
crc := ~seed
buffer := raw_data(data)
length := len(data)
for length != 0 && uintptr(buffer) & 7 != 0 {
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8);
buffer = intrinsics.ptr_offset(buffer, 1);
length -= 1;
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
buffer = intrinsics.ptr_offset(buffer, 1)
length -= 1
}
for length >= 8 {
buf := (^[8]byte)(buffer);
word := u32((^u32le)(buffer)^);
crc ~= word;
buf := (^[8]byte)(buffer)
word := u32((^u32le)(buffer)^)
crc ~= word
crc = crc32_table[7][crc & 0xff] ~
crc32_table[6][(crc >> 8) & 0xff] ~
@@ -26,21 +26,21 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
crc32_table[3][buf[4]] ~
crc32_table[2][buf[5]] ~
crc32_table[1][buf[6]] ~
crc32_table[0][buf[7]];
crc32_table[0][buf[7]]
buffer = intrinsics.ptr_offset(buffer, 8);
length -= 8;
buffer = intrinsics.ptr_offset(buffer, 8)
length -= 8
}
for length != 0 {
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8);
buffer = intrinsics.ptr_offset(buffer, 1);
length -= 1;
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
buffer = intrinsics.ptr_offset(buffer, 1)
length -= 1
}
return ~crc;
return ~crc
}
@(private)
@@ -317,7 +317,7 @@ crc32_table := [8][256]u32{
0xff6b144a, 0x33c114d4, 0xbd4e1337, 0x71e413a9, 0x7b211ab0, 0xb78b1a2e, 0x39041dcd, 0xf5ae1d53,
0x2c8e0fff, 0xe0240f61, 0x6eab0882, 0xa201081c, 0xa8c40105, 0x646e019b, 0xeae10678, 0x264b06e6,
},
};
}
+151 -151
View File
@@ -6,266 +6,266 @@ import "core:intrinsics"
@(optimization_mode="speed")
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
ADLER_CONST :: 65521;
ADLER_CONST :: 65521
buffer := raw_data(data);
a, b: u64 = u64(seed) & 0xFFFF, u64(seed) >> 16;
buf := data[:];
buffer := raw_data(data)
a, b: u64 = u64(seed) & 0xFFFF, u64(seed) >> 16
buf := data[:]
for len(buf) != 0 && uintptr(buffer) & 7 != 0 {
a = (a + u64(buf[0]));
b = (b + a);
buffer = intrinsics.ptr_offset(buffer, 1);
buf = buf[1:];
a = (a + u64(buf[0]))
b = (b + a)
buffer = intrinsics.ptr_offset(buffer, 1)
buf = buf[1:]
}
for len(buf) > 7 {
count := min(len(buf), 5552);
count := min(len(buf), 5552)
for count > 7 {
a += u64(buf[0]); b += a;
a += u64(buf[1]); b += a;
a += u64(buf[2]); b += a;
a += u64(buf[3]); b += a;
a += u64(buf[4]); b += a;
a += u64(buf[5]); b += a;
a += u64(buf[6]); b += a;
a += u64(buf[7]); b += a;
a += u64(buf[0]); b += a
a += u64(buf[1]); b += a
a += u64(buf[2]); b += a
a += u64(buf[3]); b += a
a += u64(buf[4]); b += a
a += u64(buf[5]); b += a
a += u64(buf[6]); b += a
a += u64(buf[7]); b += a
buf = buf[8:];
count -= 8;
buf = buf[8:]
count -= 8
}
a %= ADLER_CONST;
b %= ADLER_CONST;
a %= ADLER_CONST
b %= ADLER_CONST
}
for len(buf) != 0 {
a = (a + u64(buf[0])) % ADLER_CONST;
b = (b + a) % ADLER_CONST;
buf = buf[1:];
a = (a + u64(buf[0])) % ADLER_CONST
b = (b + a) % ADLER_CONST
buf = buf[1:]
}
return (u32(b) << 16) | u32(a);
return (u32(b) << 16) | u32(a)
}
@(optimization_mode="speed")
djb2 :: proc(data: []byte) -> u32 {
hash: u32 = 5381;
hash: u32 = 5381
for b in data {
hash = (hash << 5) + hash + u32(b); // hash * 33 + u32(b)
hash = (hash << 5) + hash + u32(b) // hash * 33 + u32(b)
}
return hash;
return hash
}
@(optimization_mode="speed")
fnv32 :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5;
h: u32 = 0x811c9dc5
for b in data {
h = (h * 0x01000193) ~ u32(b);
h = (h * 0x01000193) ~ u32(b)
}
return h;
return h
}
@(optimization_mode="speed")
fnv64 :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325;
h: u64 = 0xcbf29ce484222325
for b in data {
h = (h * 0x100000001b3) ~ u64(b);
h = (h * 0x100000001b3) ~ u64(b)
}
return h;
return h
}
@(optimization_mode="speed")
fnv32a :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5;
h: u32 = 0x811c9dc5
for b in data {
h = (h ~ u32(b)) * 0x01000193;
h = (h ~ u32(b)) * 0x01000193
}
return h;
return h
}
@(optimization_mode="speed")
fnv64a :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325;
h: u64 = 0xcbf29ce484222325
for b in data {
h = (h ~ u64(b)) * 0x100000001b3;
h = (h ~ u64(b)) * 0x100000001b3
}
return h;
return h
}
@(optimization_mode="speed")
jenkins :: proc(data: []byte) -> u32 {
hash: u32 = 0;
hash: u32 = 0
for b in data {
hash += u32(b);
hash += hash << 10;
hash ~= hash >> 6;
hash += u32(b)
hash += hash << 10
hash ~= hash >> 6
}
hash += hash << 3;
hash ~= hash >> 11;
hash += hash << 15;
return hash;
hash += hash << 3
hash ~= hash >> 11
hash += hash << 15
return hash
}
@(optimization_mode="speed")
murmur32 :: proc(data: []byte) -> u32 {
c1_32: u32 : 0xcc9e2d51;
c2_32: u32 : 0x1b873593;
c1_32: u32 : 0xcc9e2d51
c2_32: u32 : 0x1b873593
h1: u32 = 0;
nblocks := len(data)/4;
p := raw_data(data);
p1 := mem.ptr_offset(p, 4*nblocks);
h1: u32 = 0
nblocks := len(data)/4
p := raw_data(data)
p1 := mem.ptr_offset(p, 4*nblocks)
for ; p < p1; p = mem.ptr_offset(p, 4) {
k1 := (cast(^u32)p)^;
k1 := (cast(^u32)p)^
k1 *= c1_32;
k1 = (k1 << 15) | (k1 >> 17);
k1 *= c2_32;
k1 *= c1_32
k1 = (k1 << 15) | (k1 >> 17)
k1 *= c2_32
h1 ~= k1;
h1 = (h1 << 13) | (h1 >> 19);
h1 = h1*5 + 0xe6546b64;
h1 ~= k1
h1 = (h1 << 13) | (h1 >> 19)
h1 = h1*5 + 0xe6546b64
}
tail := data[nblocks*4:];
k1: u32;
tail := data[nblocks*4:]
k1: u32
switch len(tail)&3 {
case 3:
k1 ~= u32(tail[2]) << 16;
fallthrough;
k1 ~= u32(tail[2]) << 16
fallthrough
case 2:
k1 ~= u32(tail[2]) << 8;
fallthrough;
k1 ~= u32(tail[2]) << 8
fallthrough
case 1:
k1 ~= u32(tail[0]);
k1 *= c1_32;
k1 = (k1 << 15) | (k1 >> 17) ;
k1 *= c2_32;
h1 ~= k1;
k1 ~= u32(tail[0])
k1 *= c1_32
k1 = (k1 << 15) | (k1 >> 17)
k1 *= c2_32
h1 ~= k1
}
h1 ~= u32(len(data));
h1 ~= u32(len(data))
h1 ~= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ~= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ~= h1 >> 16;
h1 ~= h1 >> 16
h1 *= 0x85ebca6b
h1 ~= h1 >> 13
h1 *= 0xc2b2ae35
h1 ~= h1 >> 16
return h1;
return h1
}
@(optimization_mode="speed")
murmur64 :: proc(data: []byte) -> u64 {
SEED :: 0x9747b28c;
SEED :: 0x9747b28c
when size_of(int) == 8 {
m :: 0xc6a4a7935bd1e995;
r :: 47;
m :: 0xc6a4a7935bd1e995
r :: 47
h: u64 = SEED ~ (u64(len(data)) * m);
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64));
h: u64 = SEED ~ (u64(len(data)) * m)
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64))
for _, i in data64 {
k := data64[i];
k := data64[i]
k *= m;
k ~= k>>r;
k *= m;
k *= m
k ~= k>>r
k *= m
h ~= k;
h *= m;
h ~= k
h *= m
}
switch len(data)&7 {
case 7: h ~= u64(data[6]) << 48; fallthrough;
case 6: h ~= u64(data[5]) << 40; fallthrough;
case 5: h ~= u64(data[4]) << 32; fallthrough;
case 4: h ~= u64(data[3]) << 24; fallthrough;
case 3: h ~= u64(data[2]) << 16; fallthrough;
case 2: h ~= u64(data[1]) << 8; fallthrough;
case 7: h ~= u64(data[6]) << 48; fallthrough
case 6: h ~= u64(data[5]) << 40; fallthrough
case 5: h ~= u64(data[4]) << 32; fallthrough
case 4: h ~= u64(data[3]) << 24; fallthrough
case 3: h ~= u64(data[2]) << 16; fallthrough
case 2: h ~= u64(data[1]) << 8; fallthrough
case 1:
h ~= u64(data[0]);
h *= m;
h ~= u64(data[0])
h *= m
}
h ~= h>>r;
h *= m;
h ~= h>>r;
h ~= h>>r
h *= m
h ~= h>>r
return h;
return h
} else {
m :: 0x5bd1e995;
r :: 24;
m :: 0x5bd1e995
r :: 24
h1 := u32(SEED) ~ u32(len(data));
h2 := u32(SEED) >> 32;
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32));
len := len(data);
i := 0;
h1 := u32(SEED) ~ u32(len(data))
h2 := u32(SEED) >> 32
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
len := len(data)
i := 0
for len >= 8 {
k1, k2: u32;
k1 = data32[i]; i += 1;
k1 *= m;
k1 ~= k1>>r;
k1 *= m;
h1 *= m;
h1 ~= k1;
len -= 4;
k1, k2: u32
k1 = data32[i]; i += 1
k1 *= m
k1 ~= k1>>r
k1 *= m
h1 *= m
h1 ~= k1
len -= 4
k2 = data32[i]; i += 1;
k2 *= m;
k2 ~= k2>>r;
k2 *= m;
h2 *= m;
h2 ~= k2;
len -= 4;
k2 = data32[i]; i += 1
k2 *= m
k2 ~= k2>>r
k2 *= m
h2 *= m
h2 ~= k2
len -= 4
}
if len >= 4 {
k1: u32;
k1 = data32[i]; i += 1;
k1 *= m;
k1 ~= k1>>r;
k1 *= m;
h1 *= m;
h1 ~= k1;
len -= 4;
k1: u32
k1 = data32[i]; i += 1
k1 *= m
k1 ~= k1>>r
k1 *= m
h1 *= m
h1 ~= k1
len -= 4
}
// TODO(bill): Fix this
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3];
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
switch len {
case 3:
h2 ~= u32(data8[2]) << 16;
fallthrough;
h2 ~= u32(data8[2]) << 16
fallthrough
case 2:
h2 ~= u32(data8[1]) << 8;
fallthrough;
h2 ~= u32(data8[1]) << 8
fallthrough
case 1:
h2 ~= u32(data8[0]);
h2 *= m;
h2 ~= u32(data8[0])
h2 *= m
}
h1 ~= h2>>18;
h1 *= m;
h2 ~= h1>>22;
h2 *= m;
h1 ~= h2>>17;
h1 *= m;
h2 ~= h1>>19;
h2 *= m;
h1 ~= h2>>18
h1 *= m
h2 ~= h1>>22
h2 *= m
h1 ~= h2>>17
h1 *= m
h2 ~= h1>>19
h2 *= m
return u64(h1)<<32 | u64(h2);
return u64(h1)<<32 | u64(h2)
}
}
@(optimization_mode="speed")
sdbm :: proc(data: []byte) -> u32 {
hash: u32 = 0;
hash: u32 = 0
for b in data {
hash = u32(b) + (hash<<6) + (hash<<16) - hash;
hash = u32(b) + (hash<<6) + (hash<<16) - hash
}
return hash;
return hash
}
+22 -22
View File
@@ -1,40 +1,40 @@
package hash
ginger_hash8 :: proc(x: u8) -> u8 {
h := x * 251;
h += ~(x << 3);
h ~= (x >> 1);
h += ~(x << 7);
h ~= (x >> 6);
h += (x << 2);
return h;
h := x * 251
h += ~(x << 3)
h ~= (x >> 1)
h += ~(x << 7)
h ~= (x >> 6)
h += (x << 2)
return h
}
ginger_hash16 :: proc(x: u16) -> u16 {
z := (x << 8) | (x >> 8);
h := z;
h += ~(z << 5);
h ~= (z >> 2);
h += ~(z << 13);
h ~= (z >> 10);
h += ~(z << 4);
h = (h << 10) | (h >> 10);
return h;
z := (x << 8) | (x >> 8)
h := z
h += ~(z << 5)
h ~= (z >> 2)
h += ~(z << 13)
h ~= (z >> 10)
h += ~(z << 4)
h = (h << 10) | (h >> 10)
return h
}
ginger8 :: proc(data: []byte) -> u8 {
h := ginger_hash8(0);
h := ginger_hash8(0)
for b in data {
h ~= ginger_hash8(b);
h ~= ginger_hash8(b)
}
return h;
return h
}
ginger16 :: proc(data: []byte) -> u16 {
h := ginger_hash16(0);
h := ginger_hash16(0)
for b in data {
h ~= ginger_hash16(u16(b));
h ~= ginger_hash16(u16(b))
}
return h;
return h
}
+35 -35
View File
@@ -110,7 +110,7 @@ Option :: enum {
do_not_expand_indexed,
do_not_expand_channels,
}
Options :: distinct bit_set[Option];
Options :: distinct bit_set[Option]
Error :: enum {
Invalid_PNG_Signature,
@@ -138,8 +138,8 @@ Error :: enum {
*/
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height;
return;
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
return
}
/*
@@ -154,61 +154,61 @@ Channel :: enum u8 {
}
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
ok = false;
t: bytes.Buffer;
ok = false
t: bytes.Buffer
idx := int(channel);
idx := int(channel)
if img.channels == 2 && idx == 4 {
// Alpha requested, which in a two channel image is index 2: G.
idx = 2;
idx = 2
}
if idx > img.channels {
return {}, false;
return {}, false
}
switch img.depth {
case 8:
buffer_size := compute_buffer_size(img.width, img.height, 1, 8);
t = bytes.Buffer{};
resize(&t.buf, buffer_size);
buffer_size := compute_buffer_size(img.width, img.height, 1, 8)
t = bytes.Buffer{}
resize(&t.buf, buffer_size)
i := bytes.buffer_to_bytes(&img.pixels);
o := bytes.buffer_to_bytes(&t);
i := bytes.buffer_to_bytes(&img.pixels)
o := bytes.buffer_to_bytes(&t)
for len(i) > 0 {
o[0] = i[idx];
i = i[img.channels:];
o = o[1:];
o[0] = i[idx]
i = i[img.channels:]
o = o[1:]
}
case 16:
buffer_size := compute_buffer_size(img.width, img.height, 2, 8);
t = bytes.Buffer{};
resize(&t.buf, buffer_size);
buffer_size := compute_buffer_size(img.width, img.height, 2, 8)
t = bytes.Buffer{}
resize(&t.buf, buffer_size)
i := mem.slice_data_cast([]u16, img.pixels.buf[:]);
o := mem.slice_data_cast([]u16, t.buf[:]);
i := mem.slice_data_cast([]u16, img.pixels.buf[:])
o := mem.slice_data_cast([]u16, t.buf[:])
for len(i) > 0 {
o[0] = i[idx];
i = i[img.channels:];
o = o[1:];
o[0] = i[idx]
i = i[img.channels:]
o = o[1:]
}
case 1, 2, 4:
// We shouldn't see this case, as the loader already turns these into 8-bit.
return {}, false;
return {}, false
}
res = new(Image);
res.width = img.width;
res.height = img.height;
res.channels = 1;
res.depth = img.depth;
res.pixels = t;
res.background = img.background;
res.metadata_ptr = img.metadata_ptr;
res.metadata_type = img.metadata_type;
res = new(Image)
res.width = img.width
res.height = img.height
res.channels = 1
res.depth = img.depth
res.pixels = t
res.background = img.background
res.metadata_ptr = img.metadata_ptr
res.metadata_type = img.metadata_type
return res, true;
return res, true
}
+156 -156
View File
@@ -23,125 +23,125 @@ import "core:mem"
import "core:os"
main :: proc() {
track := mem.Tracking_Allocator{};
mem.tracking_allocator_init(&track, context.allocator);
track := mem.Tracking_Allocator{}
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track);
context.allocator = mem.tracking_allocator(&track)
demo();
demo()
if len(track.allocation_map) > 0 {
fmt.println("Leaks:");
fmt.println("Leaks:")
for _, v in track.allocation_map {
fmt.printf("\t%v\n\n", v);
fmt.printf("\t%v\n\n", v)
}
}
}
demo :: proc() {
file: string;
file: string
options := image.Options{}; // {.return_metadata};
err: compress.Error;
img: ^image.Image;
options := image.Options{} // {.return_metadata};
err: compress.Error
img: ^image.Image
file = "../../../misc/logo-slim.png";
file = "../../../misc/logo-slim.png"
img, err = load(file, options);
defer destroy(img);
img, err = load(file, options)
defer destroy(img)
if err != nil {
fmt.printf("Trying to read PNG file %v returned %v\n", file, err);
fmt.printf("Trying to read PNG file %v returned %v\n", file, err)
} else {
v: ^Info;
v: ^Info
fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth);
fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth)
if img.metadata_ptr != nil && img.metadata_type == Info {
v = (^Info)(img.metadata_ptr);
v = (^Info)(img.metadata_ptr)
// Handle ancillary chunks as you wish.
// We provide helper functions for a few types.
for c in v.chunks {
#partial switch c.header.type {
case .tIME:
t, _ := core_time(c);
fmt.printf("[tIME]: %v\n", t);
t, _ := core_time(c)
fmt.printf("[tIME]: %v\n", t)
case .gAMA:
fmt.printf("[gAMA]: %v\n", gamma(c));
fmt.printf("[gAMA]: %v\n", gamma(c))
case .pHYs:
phys := phys(c);
phys := phys(c)
if phys.unit == .Meter {
xm := f32(img.width) / f32(phys.ppu_x);
ym := f32(img.height) / f32(phys.ppu_y);
dpi_x, dpi_y := phys_to_dpi(phys);
fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y);
fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y);
fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym);
xm := f32(img.width) / f32(phys.ppu_x)
ym := f32(img.height) / f32(phys.ppu_y)
dpi_x, dpi_y := phys_to_dpi(phys)
fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y)
fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y)
fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym)
} else {
fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y);
fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y)
}
case .iTXt, .zTXt, .tEXt:
res, ok_text := text(c);
res, ok_text := text(c)
if ok_text {
if c.header.type == .iTXt {
fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text);
fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text)
} else {
fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text);
fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text)
}
}
defer text_destroy(res);
defer text_destroy(res)
case .bKGD:
fmt.printf("[bKGD] %v\n", img.background);
fmt.printf("[bKGD] %v\n", img.background)
case .eXIf:
res, ok_exif := exif(c);
res, ok_exif := exif(c)
if ok_exif {
/*
Other than checking the signature and byte order, we don't handle Exif data.
If you wish to interpret it, pass it to an Exif parser.
*/
fmt.printf("[eXIf] %v\n", res);
fmt.printf("[eXIf] %v\n", res)
}
case .PLTE:
plte, plte_ok := plte(c);
plte, plte_ok := plte(c)
if plte_ok {
fmt.printf("[PLTE] %v\n", plte);
fmt.printf("[PLTE] %v\n", plte)
} else {
fmt.printf("[PLTE] Error\n");
fmt.printf("[PLTE] Error\n")
}
case .hIST:
res, ok_hist := hist(c);
res, ok_hist := hist(c)
if ok_hist {
fmt.printf("[hIST] %v\n", res);
fmt.printf("[hIST] %v\n", res)
}
case .cHRM:
res, ok_chrm := chrm(c);
res, ok_chrm := chrm(c)
if ok_chrm {
fmt.printf("[cHRM] %v\n", res);
fmt.printf("[cHRM] %v\n", res)
}
case .sPLT:
res, ok_splt := splt(c);
res, ok_splt := splt(c)
if ok_splt {
fmt.printf("[sPLT] %v\n", res);
fmt.printf("[sPLT] %v\n", res)
}
splt_destroy(res);
splt_destroy(res)
case .sBIT:
if res, ok_sbit := sbit(c); ok_sbit {
fmt.printf("[sBIT] %v\n", res);
fmt.printf("[sBIT] %v\n", res)
}
case .iCCP:
res, ok_iccp := iccp(c);
res, ok_iccp := iccp(c)
if ok_iccp {
fmt.printf("[iCCP] %v\n", res);
fmt.printf("[iCCP] %v\n", res)
}
iccp_destroy(res);
iccp_destroy(res)
case .sRGB:
if res, ok_srgb := srgb(c); ok_srgb {
fmt.printf("[sRGB] Rendering intent: %v\n", res);
fmt.printf("[sRGB] Rendering intent: %v\n", res)
}
case:
type := c.header.type;
name := chunk_type_to_name(&type);
fmt.printf("[%v]: %v\n", name, c.data);
type := c.header.type
name := chunk_type_to_name(&type)
fmt.printf("[%v]: %v\n", name, c.data)
}
}
}
@@ -149,10 +149,10 @@ demo :: proc() {
if err == nil && .do_not_decompress_image not_in options && .info not_in options {
if ok := write_image_as_ppm("out.ppm", img); ok {
fmt.println("Saved decoded image.");
fmt.println("Saved decoded image.")
} else {
fmt.println("Error saving out.ppm.");
fmt.println(img);
fmt.println("Error saving out.ppm.")
fmt.println(img)
}
}
}
@@ -162,193 +162,193 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
_bg :: proc(bg: Maybe([3]u16), x, y: int, high := true) -> (res: [3]u16) {
if v, ok := bg.?; ok {
res = v;
res = v
} else {
if high {
l := u16(30 * 256 + 30);
l := u16(30 * 256 + 30)
if (x & 4 == 0) ~ (y & 4 == 0) {
res = [3]u16{l, 0, l};
res = [3]u16{l, 0, l}
} else {
res = [3]u16{l >> 1, 0, l >> 1};
res = [3]u16{l >> 1, 0, l >> 1}
}
} else {
if (x & 4 == 0) ~ (y & 4 == 0) {
res = [3]u16{30, 30, 30};
res = [3]u16{30, 30, 30}
} else {
res = [3]u16{15, 15, 15};
res = [3]u16{15, 15, 15}
}
}
}
return;
return
}
// profiler.timed_proc();
using image;
using os;
using image
using os
flags: int = O_WRONLY|O_CREATE|O_TRUNC;
flags: int = O_WRONLY|O_CREATE|O_TRUNC
img := image;
img := image
// PBM 16-bit images are big endian
when ODIN_ENDIAN == "little" {
if img.depth == 16 {
// The pixel components are in Big Endian. Let's byteswap back.
input := mem.slice_data_cast([]u16, img.pixels.buf[:]);
output := mem.slice_data_cast([]u16be, img.pixels.buf[:]);
input := mem.slice_data_cast([]u16, img.pixels.buf[:])
output := mem.slice_data_cast([]u16be, img.pixels.buf[:])
#no_bounds_check for v, i in input {
output[i] = u16be(v);
output[i] = u16be(v)
}
}
}
pix := bytes.buffer_to_bytes(&img.pixels);
pix := bytes.buffer_to_bytes(&img.pixels)
if len(pix) == 0 || len(pix) < image.width * image.height * int(image.channels) {
return false;
return false
}
mode: int = 0;
mode: int = 0
when ODIN_OS == "linux" || ODIN_OS == "darwin" {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
}
fd, err := open(filename, flags, mode);
fd, err := open(filename, flags, mode)
if err != 0 {
return false;
return false
}
defer close(fd);
defer close(fd)
write_string(fd,
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)),
);
)
if channels == 3 {
// We don't handle transparency here...
write_ptr(fd, raw_data(pix), len(pix));
write_ptr(fd, raw_data(pix), len(pix))
} else {
bpp := depth == 16 ? 2 : 1;
bytes_needed := width * height * 3 * bpp;
bpp := depth == 16 ? 2 : 1
bytes_needed := width * height * 3 * bpp
op := bytes.Buffer{};
bytes.buffer_init_allocator(&op, bytes_needed, bytes_needed);
defer bytes.buffer_destroy(&op);
op := bytes.Buffer{}
bytes.buffer_init_allocator(&op, bytes_needed, bytes_needed)
defer bytes.buffer_destroy(&op)
if channels == 1 {
if depth == 16 {
assert(len(pix) == width * height * 2);
p16 := mem.slice_data_cast([]u16, pix);
o16 := mem.slice_data_cast([]u16, op.buf[:]);
assert(len(pix) == width * height * 2)
p16 := mem.slice_data_cast([]u16, pix)
o16 := mem.slice_data_cast([]u16, op.buf[:])
#no_bounds_check for len(p16) != 0 {
r := u16(p16[0]);
o16[0] = r;
o16[1] = r;
o16[2] = r;
p16 = p16[1:];
o16 = o16[3:];
r := u16(p16[0])
o16[0] = r
o16[1] = r
o16[2] = r
p16 = p16[1:]
o16 = o16[3:]
}
} else {
o := 0;
o := 0
for i := 0; i < len(pix); i += 1 {
r := pix[i];
op.buf[o ] = r;
op.buf[o+1] = r;
op.buf[o+2] = r;
o += 3;
r := pix[i]
op.buf[o ] = r
op.buf[o+1] = r
op.buf[o+2] = r
o += 3
}
}
write_ptr(fd, raw_data(op.buf), len(op.buf));
write_ptr(fd, raw_data(op.buf), len(op.buf))
} else if channels == 2 {
if depth == 16 {
p16 := mem.slice_data_cast([]u16, pix);
o16 := mem.slice_data_cast([]u16, op.buf[:]);
p16 := mem.slice_data_cast([]u16, pix)
o16 := mem.slice_data_cast([]u16, op.buf[:])
bgcol := img.background;
bgcol := img.background
#no_bounds_check for len(p16) != 0 {
r := f64(u16(p16[0]));
bg: f64;
r := f64(u16(p16[0]))
bg: f64
if bgcol != nil {
v := bgcol.([3]u16)[0];
bg = f64(v);
v := bgcol.([3]u16)[0]
bg = f64(v)
}
a := f64(u16(p16[1])) / 65535.0;
l := (a * r) + (1 - a) * bg;
a := f64(u16(p16[1])) / 65535.0
l := (a * r) + (1 - a) * bg
o16[0] = u16(l);
o16[1] = u16(l);
o16[2] = u16(l);
o16[0] = u16(l)
o16[1] = u16(l)
o16[2] = u16(l)
p16 = p16[2:];
o16 = o16[3:];
p16 = p16[2:]
o16 = o16[3:]
}
} else {
o := 0;
o := 0
for i := 0; i < len(pix); i += 2 {
r := pix[i]; a := pix[i+1]; a1 := f32(a) / 255.0;
c := u8(f32(r) * a1);
op.buf[o ] = c;
op.buf[o+1] = c;
op.buf[o+2] = c;
o += 3;
r := pix[i]; a := pix[i+1]; a1 := f32(a) / 255.0
c := u8(f32(r) * a1)
op.buf[o ] = c
op.buf[o+1] = c
op.buf[o+2] = c
o += 3
}
}
write_ptr(fd, raw_data(op.buf), len(op.buf));
write_ptr(fd, raw_data(op.buf), len(op.buf))
} else if channels == 4 {
if depth == 16 {
p16 := mem.slice_data_cast([]u16be, pix);
o16 := mem.slice_data_cast([]u16be, op.buf[:]);
p16 := mem.slice_data_cast([]u16be, pix)
o16 := mem.slice_data_cast([]u16be, op.buf[:])
#no_bounds_check for len(p16) != 0 {
bg := _bg(img.background, 0, 0);
r := f32(p16[0]);
g := f32(p16[1]);
b := f32(p16[2]);
a := f32(p16[3]) / 65535.0;
bg := _bg(img.background, 0, 0)
r := f32(p16[0])
g := f32(p16[1])
b := f32(p16[2])
a := f32(p16[3]) / 65535.0
lr := (a * r) + (1 - a) * f32(bg[0]);
lg := (a * g) + (1 - a) * f32(bg[1]);
lb := (a * b) + (1 - a) * f32(bg[2]);
lr := (a * r) + (1 - a) * f32(bg[0])
lg := (a * g) + (1 - a) * f32(bg[1])
lb := (a * b) + (1 - a) * f32(bg[2])
o16[0] = u16be(lr);
o16[1] = u16be(lg);
o16[2] = u16be(lb);
o16[0] = u16be(lr)
o16[1] = u16be(lg)
o16[2] = u16be(lb)
p16 = p16[4:];
o16 = o16[3:];
p16 = p16[4:]
o16 = o16[3:]
}
} else {
o := 0;
o := 0
for i := 0; i < len(pix); i += 4 {
x := (i / 4) % width;
y := i / width / 4;
x := (i / 4) % width
y := i / width / 4
_b := _bg(img.background, x, y, false);
bgcol := [3]u8{u8(_b[0]), u8(_b[1]), u8(_b[2])};
_b := _bg(img.background, x, y, false)
bgcol := [3]u8{u8(_b[0]), u8(_b[1]), u8(_b[2])}
r := f32(pix[i]);
g := f32(pix[i+1]);
b := f32(pix[i+2]);
a := f32(pix[i+3]) / 255.0;
r := f32(pix[i])
g := f32(pix[i+1])
b := f32(pix[i+2])
a := f32(pix[i+3]) / 255.0
lr := u8(f32(r) * a + (1 - a) * f32(bgcol[0]));
lg := u8(f32(g) * a + (1 - a) * f32(bgcol[1]));
lb := u8(f32(b) * a + (1 - a) * f32(bgcol[2]));
op.buf[o ] = lr;
op.buf[o+1] = lg;
op.buf[o+2] = lb;
o += 3;
lr := u8(f32(r) * a + (1 - a) * f32(bgcol[0]))
lg := u8(f32(g) * a + (1 - a) * f32(bgcol[1]))
lb := u8(f32(b) * a + (1 - a) * f32(bgcol[2]))
op.buf[o ] = lr
op.buf[o+1] = lg
op.buf[o+2] = lb
o += 3
}
}
write_ptr(fd, raw_data(op.buf), len(op.buf));
write_ptr(fd, raw_data(op.buf), len(op.buf))
} else {
return false;
return false
}
}
return true;
return true
}
+200 -200
View File
@@ -30,12 +30,12 @@ destroy :: proc(img: ^Image) {
Nothing to do.
Load must've returned with an error.
*/
return;
return
}
bytes.buffer_destroy(&img.pixels);
bytes.buffer_destroy(&img.pixels)
// Clean up Info.
free(img.metadata_ptr);
free(img.metadata_ptr)
/*
We don't need to do anything for the individual chunks.
@@ -43,7 +43,7 @@ destroy :: proc(img: ^Image) {
See read_chunk.
*/
free(img);
free(img)
}
/*
@@ -51,259 +51,259 @@ destroy :: proc(img: ^Image) {
*/
gamma :: proc(c: Chunk) -> f32 {
assert(c.header.type == .gAMA);
res := (^gAMA)(raw_data(c.data))^;
assert(c.header.type == .gAMA)
res := (^gAMA)(raw_data(c.data))^
when true {
// Returns the wrong result on old backend
// Fixed for -llvm-api
return f32(res.gamma_100k) / 100_000.0;
return f32(res.gamma_100k) / 100_000.0
} else {
return f32(u32(res.gamma_100k)) / 100_000.0;
return f32(u32(res.gamma_100k)) / 100_000.0
}
}
INCHES_PER_METER :: 1000.0 / 25.4;
INCHES_PER_METER :: 1000.0 / 25.4
phys :: proc(c: Chunk) -> pHYs {
assert(c.header.type == .pHYs);
res := (^pHYs)(raw_data(c.data))^;
return res;
assert(c.header.type == .pHYs)
res := (^pHYs)(raw_data(c.data))^
return res
}
phys_to_dpi :: proc(p: pHYs) -> (x_dpi, y_dpi: f32) {
return f32(p.ppu_x) / INCHES_PER_METER, f32(p.ppu_y) / INCHES_PER_METER;
return f32(p.ppu_x) / INCHES_PER_METER, f32(p.ppu_y) / INCHES_PER_METER
}
time :: proc(c: Chunk) -> tIME {
assert(c.header.type == .tIME);
res := (^tIME)(raw_data(c.data))^;
return res;
assert(c.header.type == .tIME)
res := (^tIME)(raw_data(c.data))^
return res
}
core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) {
png_time := time(c);
using png_time;
png_time := time(c)
using png_time
return coretime.datetime_to_time(
int(year), int(month), int(day),
int(hour), int(minute), int(second),
);
)
}
text :: proc(c: Chunk) -> (res: Text, ok: bool) {
#partial switch c.header.type {
case .tEXt:
ok = true;
ok = true
fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator);
fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator)
if len(fields) == 2 {
res.keyword = strings.clone(string(fields[0]));
res.text = strings.clone(string(fields[1]));
res.keyword = strings.clone(string(fields[0]))
res.text = strings.clone(string(fields[1]))
} else {
ok = false;
ok = false
}
return;
return
case .zTXt:
ok = true;
ok = true
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator);
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
if len(fields) != 3 || len(fields[1]) != 0 {
// Compression method must be 0=Deflate, which thanks to the split above turns
// into an empty slice
ok = false; return;
ok = false; return
}
// Set up ZLIB context and decompress text payload.
buf: bytes.Buffer;
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf);
defer bytes.buffer_destroy(&buf);
buf: bytes.Buffer
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf)
defer bytes.buffer_destroy(&buf)
if zlib_error != nil {
ok = false; return;
ok = false; return
}
res.keyword = strings.clone(string(fields[0]));
res.text = strings.clone(bytes.buffer_to_string(&buf));
return;
res.keyword = strings.clone(string(fields[0]))
res.text = strings.clone(bytes.buffer_to_string(&buf))
return
case .iTXt:
ok = true;
ok = true
s := string(c.data);
null := strings.index_byte(s, 0);
s := string(c.data)
null := strings.index_byte(s, 0)
if null == -1 {
ok = false; return;
ok = false; return
}
if len(c.data) < null + 4 {
// At a minimum, including the \0 following the keyword, we require 5 more bytes.
ok = false; return;
ok = false; return
}
res.keyword = strings.clone(string(c.data[:null]));
rest := c.data[null+1:];
res.keyword = strings.clone(string(c.data[:null]))
rest := c.data[null+1:]
compression_flag := rest[:1][0];
compression_flag := rest[:1][0]
if compression_flag > 1 {
ok = false; return;
ok = false; return
}
compression_method := rest[1:2][0];
compression_method := rest[1:2][0]
if compression_flag == 1 && compression_method > 0 {
// Only Deflate is supported
ok = false; return;
ok = false; return
}
rest = rest[2:];
rest = rest[2:]
// We now expect an optional language keyword and translated keyword, both followed by a \0
null = strings.index_byte(string(rest), 0);
null = strings.index_byte(string(rest), 0)
if null == -1 {
ok = false; return;
ok = false; return
}
res.language = strings.clone(string(rest[:null]));
rest = rest[null+1:];
res.language = strings.clone(string(rest[:null]))
rest = rest[null+1:]
null = strings.index_byte(string(rest), 0);
null = strings.index_byte(string(rest), 0)
if null == -1 {
ok = false; return;
ok = false; return
}
res.keyword_localized = strings.clone(string(rest[:null]));
rest = rest[null+1:];
res.keyword_localized = strings.clone(string(rest[:null]))
rest = rest[null+1:]
if compression_flag == 0 {
res.text = strings.clone(string(rest));
res.text = strings.clone(string(rest))
} else {
// Set up ZLIB context and decompress text payload.
buf: bytes.Buffer;
zlib_error := zlib.inflate_from_byte_array(rest, &buf);
defer bytes.buffer_destroy(&buf);
buf: bytes.Buffer
zlib_error := zlib.inflate_from_byte_array(rest, &buf)
defer bytes.buffer_destroy(&buf)
if zlib_error != nil {
ok = false; return;
ok = false; return
}
res.text = strings.clone(bytes.buffer_to_string(&buf));
res.text = strings.clone(bytes.buffer_to_string(&buf))
}
return;
return
case:
// PNG text helper called with an unrecognized chunk type.
ok = false; return;
ok = false; return
}
}
text_destroy :: proc(text: Text) {
delete(text.keyword);
delete(text.keyword_localized);
delete(text.language);
delete(text.text);
delete(text.keyword)
delete(text.keyword_localized)
delete(text.language)
delete(text.text)
}
iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) {
ok = true;
ok = true
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator);
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
if len(fields[0]) < 1 || len(fields[0]) > 79 {
// Invalid profile name
ok = false; return;
ok = false; return
}
if len(fields[1]) != 0 {
// Compression method should be a zero, which the split turned into an empty slice.
ok = false; return;
ok = false; return
}
// Set up ZLIB context and decompress iCCP payload
buf: bytes.Buffer;
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf);
buf: bytes.Buffer
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf)
if zlib_error != nil {
bytes.buffer_destroy(&buf);
ok = false; return;
bytes.buffer_destroy(&buf)
ok = false; return
}
res.name = strings.clone(string(fields[0]));
res.profile = bytes.buffer_to_bytes(&buf);
res.name = strings.clone(string(fields[0]))
res.profile = bytes.buffer_to_bytes(&buf)
return;
return
}
iccp_destroy :: proc(i: iCCP) {
delete(i.name);
delete(i.name)
delete(i.profile);
delete(i.profile)
}
srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
ok = true;
ok = true
if c.header.type != .sRGB || len(c.data) != 1 {
return {}, false;
return {}, false
}
res.intent = sRGB_Rendering_Intent(c.data[0]);
res.intent = sRGB_Rendering_Intent(c.data[0])
if res.intent > max(sRGB_Rendering_Intent) {
ok = false; return;
ok = false; return
}
return;
return
}
plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {
if c.header.type != .PLTE {
return {}, false;
return {}, false
}
i := 0; j := 0; ok = true;
i := 0; j := 0; ok = true
for j < int(c.header.length) {
res.entries[i] = {c.data[j], c.data[j+1], c.data[j+2]};
i += 1; j += 3;
res.entries[i] = {c.data[j], c.data[j+1], c.data[j+2]}
i += 1; j += 3
}
res.used = u16(i);
return;
res.used = u16(i)
return
}
splt :: proc(c: Chunk) -> (res: sPLT, ok: bool) {
if c.header.type != .sPLT {
return {}, false;
return {}, false
}
ok = true;
ok = true
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator);
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator)
if len(fields) != 2 {
return {}, false;
return {}, false
}
res.depth = fields[1][0];
res.depth = fields[1][0]
if res.depth != 8 && res.depth != 16 {
return {}, false;
return {}, false
}
data := fields[1][1:];
count: int;
data := fields[1][1:]
count: int
if res.depth == 8 {
if len(data) % 6 != 0 {
return {}, false;
return {}, false
}
count = len(data) / 6;
count = len(data) / 6
if count > 256 {
return {}, false;
return {}, false
}
res.entries = mem.slice_data_cast([][4]u8, data);
res.entries = mem.slice_data_cast([][4]u8, data)
} else { // res.depth == 16
if len(data) % 10 != 0 {
return {}, false;
return {}, false
}
count = len(data) / 10;
count = len(data) / 10
if count > 256 {
return {}, false;
return {}, false
}
res.entries = mem.slice_data_cast([][4]u16, data);
res.entries = mem.slice_data_cast([][4]u16, data)
}
res.name = strings.clone(string(fields[0]));
res.used = u16(count);
res.name = strings.clone(string(fields[0]))
res.used = u16(count)
return;
return
}
splt_destroy :: proc(s: sPLT) {
delete(s.name);
delete(s.name)
}
sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) {
@@ -313,88 +313,88 @@ sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) {
*/
if len(c.data) < 1 || len(c.data) > 4 {
ok = false; return;
ok = false; return
}
ok = true;
ok = true
for i := 0; i < len(c.data); i += 1 {
res[i] = c.data[i];
res[i] = c.data[i]
}
return;
return
}
hist :: proc(c: Chunk) -> (res: hIST, ok: bool) {
if c.header.type != .hIST {
return {}, false;
return {}, false
}
if c.header.length & 1 == 1 || c.header.length > 512 {
// The entries are u16be, so the length must be even.
// At most 256 entries must be present
return {}, false;
return {}, false
}
ok = true;
data := mem.slice_data_cast([]u16be, c.data);
i := 0;
ok = true
data := mem.slice_data_cast([]u16be, c.data)
i := 0
for len(data) > 0 {
// HIST entries are u16be, we unpack them to machine format
res.entries[i] = u16(data[0]);
i += 1; data = data[1:];
res.entries[i] = u16(data[0])
i += 1; data = data[1:]
}
res.used = u16(i);
return;
res.used = u16(i)
return
}
chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) {
ok = true;
ok = true
if c.header.length != size_of(cHRM_Raw) {
return {}, false;
return {}, false
}
chrm := (^cHRM_Raw)(raw_data(c.data))^;
chrm := (^cHRM_Raw)(raw_data(c.data))^
res.w.x = f32(chrm.w.x) / 100_000.0;
res.w.y = f32(chrm.w.y) / 100_000.0;
res.r.x = f32(chrm.r.x) / 100_000.0;
res.r.y = f32(chrm.r.y) / 100_000.0;
res.g.x = f32(chrm.g.x) / 100_000.0;
res.g.y = f32(chrm.g.y) / 100_000.0;
res.b.x = f32(chrm.b.x) / 100_000.0;
res.b.y = f32(chrm.b.y) / 100_000.0;
return;
res.w.x = f32(chrm.w.x) / 100_000.0
res.w.y = f32(chrm.w.y) / 100_000.0
res.r.x = f32(chrm.r.x) / 100_000.0
res.r.y = f32(chrm.r.y) / 100_000.0
res.g.x = f32(chrm.g.x) / 100_000.0
res.g.y = f32(chrm.g.y) / 100_000.0
res.b.x = f32(chrm.b.x) / 100_000.0
res.b.y = f32(chrm.b.y) / 100_000.0
return
}
exif :: proc(c: Chunk) -> (res: Exif, ok: bool) {
ok = true;
ok = true
if len(c.data) < 4 {
ok = false; return;
ok = false; return
}
if c.data[0] == 'M' && c.data[1] == 'M' {
res.byte_order = .big_endian;
res.byte_order = .big_endian
if c.data[2] != 0 || c.data[3] != 42 {
ok = false; return;
ok = false; return
}
} else if c.data[0] == 'I' && c.data[1] == 'I' {
res.byte_order = .little_endian;
res.byte_order = .little_endian
if c.data[2] != 42 || c.data[3] != 0 {
ok = false; return;
ok = false; return
}
} else {
ok = false; return;
ok = false; return
}
res.data = c.data;
return;
res.data = c.data
return
}
/*
General helper functions
*/
compute_buffer_size :: image.compute_buffer_size;
compute_buffer_size :: image.compute_buffer_size
/*
PNG save helpers
@@ -404,59 +404,59 @@ when false {
make_chunk :: proc(c: any, t: Chunk_Type) -> (res: Chunk) {
data: []u8;
data: []u8
if v, ok := c.([]u8); ok {
data = v;
data = v
} else {
data = mem.any_to_bytes(c);
data = mem.any_to_bytes(c)
}
res.header.length = u32be(len(data));
res.header.type = t;
res.data = data;
res.header.length = u32be(len(data))
res.header.type = t
res.data = data
// CRC the type
crc := hash.crc32(mem.any_to_bytes(res.header.type));
crc := hash.crc32(mem.any_to_bytes(res.header.type))
// Extend the CRC with the data
res.crc = u32be(hash.crc32(data, crc));
return;
res.crc = u32be(hash.crc32(data, crc))
return
}
write_chunk :: proc(fd: os.Handle, chunk: Chunk) {
c := chunk;
c := chunk
// Write length + type
os.write_ptr(fd, &c.header, 8);
os.write_ptr(fd, &c.header, 8)
// Write data
os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length));
os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length))
// Write CRC32
os.write_ptr(fd, &c.crc, 4);
os.write_ptr(fd, &c.crc, 4)
}
write_image_as_png :: proc(filename: string, image: Image) -> (err: Error) {
profiler.timed_proc();
using image;
using os;
flags: int = O_WRONLY|O_CREATE|O_TRUNC;
profiler.timed_proc()
using image
using os
flags: int = O_WRONLY|O_CREATE|O_TRUNC
if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
return E_PNG.Invalid_Image_Dimensions;
return E_PNG.Invalid_Image_Dimensions
}
mode: int = 0;
mode: int = 0
when ODIN_OS == "linux" || ODIN_OS == "darwin" {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
}
fd, fderr := open(filename, flags, mode);
fd, fderr := open(filename, flags, mode)
if fderr != 0 {
return E_General.Cannot_Open_File;
return E_General.Cannot_Open_File
}
defer close(fd);
defer close(fd)
magic := Signature;
magic := Signature
write_ptr(fd, &magic, 8);
write_ptr(fd, &magic, 8)
ihdr := IHDR{
width = u32be(width),
@@ -465,61 +465,61 @@ when false {
compression_method = 0,
filter_method = 0,
interlace_method = .None,
};
}
switch channels {
case 1: ihdr.color_type = Color_Type{};
case 2: ihdr.color_type = Color_Type{.Alpha};
case 3: ihdr.color_type = Color_Type{.Color};
case 4: ihdr.color_type = Color_Type{.Color, .Alpha};
case 1: ihdr.color_type = Color_Type{}
case 2: ihdr.color_type = Color_Type{.Alpha}
case 3: ihdr.color_type = Color_Type{.Color}
case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
case:// Unhandled
return E_PNG.Unknown_Color_Type;
return E_PNG.Unknown_Color_Type
}
h := make_chunk(ihdr, .IHDR);
write_chunk(fd, h);
h := make_chunk(ihdr, .IHDR)
write_chunk(fd, h)
bytes_needed := width * height * int(channels) + height;
filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator);
defer delete(filter_bytes);
bytes_needed := width * height * int(channels) + height
filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator)
defer delete(filter_bytes)
i := 0; j := 0;
i := 0; j := 0
// Add a filter byte 0 per pixel row
for y := 0; y < height; y += 1 {
filter_bytes[j] = 0; j += 1;
filter_bytes[j] = 0; j += 1
for x := 0; x < width; x += 1 {
for z := 0; z < channels; z += 1 {
filter_bytes[j+z] = image.pixels[i+z];
filter_bytes[j+z] = image.pixels[i+z]
}
i += channels; j += channels;
i += channels; j += channels
}
}
assert(j == bytes_needed);
assert(j == bytes_needed)
a: []u8 = filter_bytes[:];
a: []u8 = filter_bytes[:]
out_buf: ^[dynamic]u8;
defer free(out_buf);
out_buf: ^[dynamic]u8
defer free(out_buf)
ctx := zlib.ZLIB_Context{
in_buf = &a,
out_buf = out_buf,
};
err = zlib.write_zlib_stream_from_memory(&ctx);
}
err = zlib.write_zlib_stream_from_memory(&ctx)
b: []u8;
b: []u8
if err == nil {
b = ctx.out_buf[:];
b = ctx.out_buf[:]
} else {
return err;
return err
}
idat := make_chunk(b, .IDAT);
idat := make_chunk(b, .IDAT)
write_chunk(fd, idat);
write_chunk(fd, idat)
iend := make_chunk([]u8{}, .IEND);
write_chunk(fd, iend);
iend := make_chunk([]u8{}, .IEND)
write_chunk(fd, iend)
return nil;
return nil
}
}
+586 -586
View File
File diff suppressed because it is too large Load Diff
+78 -78
View File
@@ -1,194 +1,194 @@
package io
to_reader :: proc(s: Stream) -> (r: Reader, ok: bool = true) {
r.stream = s;
r.stream = s
if s.stream_vtable == nil || s.impl_read == nil {
ok = false;
ok = false
}
return;
return
}
to_writer :: proc(s: Stream) -> (w: Writer, ok: bool = true) {
w.stream = s;
w.stream = s
if s.stream_vtable == nil || s.impl_write == nil {
ok = false;
ok = false
}
return;
return
}
to_closer :: proc(s: Stream) -> (c: Closer, ok: bool = true) {
c.stream = s;
c.stream = s
if s.stream_vtable == nil || s.impl_close == nil {
ok = false;
ok = false
}
return;
return
}
to_flusher :: proc(s: Stream) -> (f: Flusher, ok: bool = true) {
f.stream = s;
f.stream = s
if s.stream_vtable == nil || s.impl_flush == nil {
ok = false;
ok = false
}
return;
return
}
to_seeker :: proc(s: Stream) -> (seeker: Seeker, ok: bool = true) {
seeker.stream = s;
seeker.stream = s
if s.stream_vtable == nil || s.impl_seek == nil {
ok = false;
ok = false
}
return;
return
}
to_read_writer :: proc(s: Stream) -> (r: Read_Writer, ok: bool = true) {
r.stream = s;
r.stream = s
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil {
ok = false;
ok = false
}
return;
return
}
to_read_closer :: proc(s: Stream) -> (r: Read_Closer, ok: bool = true) {
r.stream = s;
r.stream = s
if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil {
ok = false;
ok = false
}
return;
return
}
to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, ok: bool = true) {
r.stream = s;
r.stream = s
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil {
ok = false;
ok = false
}
return;
return
}
to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, ok: bool = true) {
r.stream = s;
r.stream = s
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil {
ok = false;
ok = false
}
return;
return
}
to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, ok: bool = true) {
w.stream = s;
w.stream = s
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil {
ok = false;
ok = false
}
return;
return
}
to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, ok: bool = true) {
w.stream = s;
w.stream = s
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil {
ok = false;
ok = false
}
return;
return
}
to_reader_at :: proc(s: Stream) -> (r: Reader_At, ok: bool = true) {
r.stream = s;
r.stream = s
if s.stream_vtable == nil || s.impl_read_at == nil {
ok = false;
ok = false
}
return;
return
}
to_writer_at :: proc(s: Stream) -> (w: Writer_At, ok: bool = true) {
w.stream = s;
w.stream = s
if s.stream_vtable == nil || s.impl_write_at == nil {
ok = false;
ok = false
}
return;
return
}
to_reader_from :: proc(s: Stream) -> (r: Reader_From, ok: bool = true) {
r.stream = s;
r.stream = s
if s.stream_vtable == nil || s.impl_read_from == nil {
ok = false;
ok = false
}
return;
return
}
to_writer_to :: proc(s: Stream) -> (w: Writer_To, ok: bool = true) {
w.stream = s;
w.stream = s
if s.stream_vtable == nil || s.impl_write_to == nil {
ok = false;
ok = false
}
return;
return
}
to_write_closer :: proc(s: Stream) -> (w: Write_Closer, ok: bool = true) {
w.stream = s;
w.stream = s
if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil {
ok = false;
ok = false
}
return;
return
}
to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) {
w.stream = s;
w.stream = s
if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil {
ok = false;
ok = false
}
return;
return
}
to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, ok: bool = true) {
b.stream = s;
b.stream = s
if s.stream_vtable == nil || s.impl_read_byte == nil {
ok = false;
ok = false
if s.stream_vtable != nil && s.impl_read != nil {
ok = true;
ok = true
}
}
return;
return
}
to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, ok: bool = true) {
b.stream = s;
b.stream = s
if s.stream_vtable != nil {
if s.impl_unread_byte == nil {
ok = false;
return;
ok = false
return
}
if s.impl_read_byte != nil {
ok = true;
ok = true
} else if s.impl_read != nil {
ok = true;
ok = true
} else {
ok = false;
ok = false
}
}
return;
return
}
to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, ok: bool = true) {
b.stream = s;
b.stream = s
if s.stream_vtable == nil || s.impl_write_byte == nil {
ok = false;
ok = false
if s.stream_vtable != nil && s.impl_write != nil {
ok = true;
ok = true
}
}
return;
return
}
to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, ok: bool = true) {
r.stream = s;
r.stream = s
if s.stream_vtable == nil || s.impl_read_rune == nil {
ok = false;
ok = false
if s.stream_vtable != nil && s.impl_read != nil {
ok = true;
ok = true
}
}
return;
return
}
to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, ok: bool = true) {
r.stream = s;
r.stream = s
if s.stream_vtable != nil {
if s.impl_unread_rune == nil {
ok = false;
return;
ok = false
return
}
if s.impl_read_rune != nil {
ok = true;
ok = true
} else if s.impl_read != nil {
ok = true;
ok = true
} else {
ok = false;
ok = false
}
} else {
ok = false;
ok = false
}
return;
return
}
+171 -171
View File
@@ -50,23 +50,23 @@ Error :: enum i32 {
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;
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 {
@@ -99,109 +99,109 @@ Stream_VTable :: struct {
}
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};
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};
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};
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};
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};
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};
Rune_Reader :: struct {using stream: Stream}
Rune_Scanner :: struct {using stream: Stream}
destroy :: proc(s: Stream) -> Error {
close_err := close({s});
close_err := close({s})
if s.stream_vtable != nil && s.impl_destroy != nil {
return s->impl_destroy();
return s->impl_destroy()
}
if close_err != .None {
return close_err;
return close_err
}
return .Empty;
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 s->impl_read(p)
}
return 0, .Empty;
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 s->impl_write(p)
}
return 0, .Empty;
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 s->impl_seek(offset, whence)
}
return 0, .Empty;
return 0, .Empty
}
close :: proc(s: Closer) -> Error {
if s.stream_vtable != nil && s.impl_close != nil {
return s->impl_close();
return s->impl_close()
}
// Instead of .Empty, .None is fine in this case
return .None;
return .None
}
flush :: proc(s: Flusher) -> Error {
if s.stream_vtable != nil && s.impl_flush != nil {
return s->impl_flush();
return s->impl_flush()
}
// Instead of .Empty, .None is fine in this case
return .None;
return .None
}
size :: proc(s: Stream) -> i64 {
if s.stream_vtable == nil {
return 0;
return 0
}
if s.impl_size != nil {
return s->impl_size();
return s->impl_size()
}
if s.impl_seek == nil {
return 0;
return 0
}
curr, end: i64;
err: Error;
curr, end: i64
err: Error
if curr, err = s->impl_seek(0, .Current); err != nil {
return 0;
return 0
}
if end, err = s->impl_seek(0, .End); err != nil {
return 0;
return 0
}
if _, err = s->impl_seek(curr, .Start); err != nil {
return 0;
return 0
}
return end;
return end
}
@@ -209,225 +209,225 @@ size :: proc(s: Stream) -> i64 {
read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) {
if r.stream_vtable == nil {
return 0, .Empty;
return 0, .Empty
}
if r.impl_read_at != nil {
return r->impl_read_at(p, offset);
return r->impl_read_at(p, offset)
}
if r.impl_seek == nil || r.impl_read == nil {
return 0, .Empty;
return 0, .Empty
}
curr_offset: i64;
curr_offset, err = r->impl_seek(offset, .Current);
curr_offset: i64
curr_offset, err = r->impl_seek(offset, .Current)
if err != nil {
return 0, err;
return 0, err
}
n, err = r->impl_read(p);
_, err1 := r->impl_seek(curr_offset, .Start);
n, err = r->impl_read(p)
_, err1 := r->impl_seek(curr_offset, .Start)
if err1 != nil && err == nil {
err = err1;
err = err1
}
return;
return
}
write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) {
if w.stream_vtable == nil {
return 0, .Empty;
return 0, .Empty
}
if w.impl_write_at != nil {
return w->impl_write_at(p, offset);
return w->impl_write_at(p, offset)
}
if w.impl_seek == nil || w.impl_write == nil {
return 0, .Empty;
return 0, .Empty
}
curr_offset: i64;
curr_offset, err = w->impl_seek(offset, .Current);
curr_offset: i64
curr_offset, err = w->impl_seek(offset, .Current)
if err != nil {
return 0, err;
return 0, err
}
n, err = w->impl_write(p);
_, err1 := w->impl_seek(curr_offset, .Start);
n, err = w->impl_write(p)
_, err1 := w->impl_seek(curr_offset, .Start)
if err1 != nil && err == nil {
err = err1;
err = err1
}
return;
return
}
write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) {
if r.stream_vtable == nil || w.stream_vtable == nil {
return 0, .Empty;
return 0, .Empty
}
if r.impl_write_to != nil {
return r->impl_write_to(w);
return r->impl_write_to(w)
}
return 0, .Empty;
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;
return 0, .Empty
}
if r.impl_read_from != nil {
return w->impl_read_from(r);
return w->impl_read_from(r)
}
return 0, .Empty;
return 0, .Empty
}
read_byte :: proc(r: Byte_Reader) -> (byte, Error) {
if r.stream_vtable == nil {
return 0, .Empty;
return 0, .Empty
}
if r.impl_read_byte != nil {
return r->impl_read_byte();
return r->impl_read_byte()
}
if r.impl_read == nil {
return 0, .Empty;
return 0, .Empty
}
b: [1]byte;
_, err := r->impl_read(b[:]);
return b[0], err;
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(w, c);
return _write_byte(w, c)
}
write_byte_to_writer :: proc(w: Writer, c: byte) -> Error {
return _write_byte(auto_cast w, c);
return _write_byte(auto_cast w, c)
}
@(private)
_write_byte :: proc(w: Byte_Writer, c: byte) -> Error {
if w.stream_vtable == nil {
return .Empty;
return .Empty
}
if w.impl_write_byte != nil {
return w->impl_write_byte(c);
return w->impl_write_byte(c)
}
if w.impl_write == nil {
return .Empty;
return .Empty
}
b := [1]byte{c};
_, err := w->impl_write(b[:]);
return err;
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;
return 0, 0, .Empty
}
if br.impl_read_rune != nil {
return br->impl_read_rune();
return br->impl_read_rune()
}
if br.impl_read == nil {
return 0, 0, .Empty;
return 0, 0, .Empty
}
b: [utf8.UTF_MAX]byte;
_, err = br->impl_read(b[:1]);
b: [utf8.UTF_MAX]byte
_, err = br->impl_read(b[:1])
s0 := b[0];
ch = rune(s0);
size = 1;
s0 := b[0]
ch = rune(s0)
size = 1
if err != nil {
return;
return
}
if ch < utf8.RUNE_SELF {
return;
return
}
x := utf8.accept_sizes[s0];
x := utf8.accept_sizes[s0]
if x >= 0xf0 {
mask := rune(x) << 31 >> 31;
ch = ch &~ mask | utf8.RUNE_ERROR&mask;
return;
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]);
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 = utf8.RUNE_ERROR
return
}
ch, size = utf8.decode_rune(b[:sz]);
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 s->impl_unread_byte()
}
return .Empty;
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 s->impl_unread_rune()
}
return .Empty;
return .Empty
}
write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) {
return write(s, transmute([]byte)str);
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);
return s->impl_write_rune(r)
}
if r < utf8.RUNE_SELF {
err = write_byte(s, byte(r));
err = write_byte(s, byte(r))
if err == nil {
size = 1;
size = 1
}
return;
return
}
buf, w := utf8.encode_rune(r);
return write(s, buf[:w]);
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));
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;
return 0, .Short_Buffer
}
for n < min && err == nil {
nn: int;
nn, err = read(r, buf[n:]);
n += n;
nn: int
nn, err = read(r, buf[n:])
n += n
}
if n >= min {
err = nil;
err = nil
} else if n > 0 && err == .EOF {
err = .Unexpected_EOF;
err = .Unexpected_EOF
}
return;
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);
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)
@@ -435,9 +435,9 @@ copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) {
// 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");
panic("empty buffer in io.copy_buffer")
}
return _copy_buffer(dst, src, buf);
return _copy_buffer(dst, src, buf)
}
@@ -446,69 +446,69 @@ copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err
// 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 := limited_reader_init(&Limited_Reader{}, src, n);
written, err = copy(dst, nsrc);
nsrc := limited_reader_init(&Limited_Reader{}, src, n)
written, err = copy(dst, nsrc)
if written == n {
return n, nil;
return n, nil
}
if written < n && err == nil {
// src stopped early and must have been an EOF
err = .EOF;
err = .EOF
}
return;
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;
return 0, .Empty
}
if src.impl_write_to != nil {
return src->impl_write_to(dst);
return src->impl_write_to(dst)
}
if src.impl_read_from != nil {
return dst->impl_read_from(src);
return dst->impl_read_from(src)
}
buf := buf;
buf := buf
if buf == nil {
DEFAULT_SIZE :: 4 * 1024;
size := DEFAULT_SIZE;
DEFAULT_SIZE :: 4 * 1024
size := DEFAULT_SIZE
if src.stream_vtable == _limited_reader_vtable {
l := (^Limited_Reader)(src.stream_data);
l := (^Limited_Reader)(src.stream_data)
if i64(size) > l.n {
if l.n < 1 {
size = 1;
size = 1
} else {
size = int(l.n);
size = int(l.n)
}
}
}
// NOTE(bill): alloca is fine here
buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size};
buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size}
}
for {
nr, er := read(src, buf);
nr, er := read(src, buf)
if nr > 0 {
nw, ew := write(dst, buf[0:nr]);
nw, ew := write(dst, buf[0:nr])
if nw > 0 {
written += i64(nw);
written += i64(nw)
}
if ew != nil {
err = ew;
break;
err = ew
break
}
if nr != nw {
err = .Short_Write;
break;
err = .Short_Write
break
}
}
if er != nil {
if er != .EOF {
err = er;
err = er
}
break;
break
}
}
return;
return
}
+32 -32
View File
@@ -7,46 +7,46 @@ Multi_Reader :: struct {
@(private)
_multi_reader_vtable := &Stream_VTable{
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
mr := (^Multi_Reader)(s.stream_data);
mr := (^Multi_Reader)(s.stream_data)
for len(mr.readers) > 0 {
r := mr.readers[0];
n, err = read(r, p);
r := mr.readers[0]
n, err = read(r, p)
if err == .EOF {
ordered_remove(&mr.readers, 0);
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;
err = nil
}
return;
return
}
}
return 0, .EOF;
return 0, .EOF
},
};
}
multi_reader_init :: proc(mr: ^Multi_Reader, readers: ..Reader, allocator := context.allocator) -> (r: Reader) {
all_readers := make([dynamic]Reader, 0, len(readers), allocator);
all_readers := make([dynamic]Reader, 0, len(readers), allocator)
for w in readers {
if w.stream_vtable == _multi_reader_vtable {
other := (^Multi_Reader)(w.stream_data);
append(&all_readers, ..other.readers[:]);
other := (^Multi_Reader)(w.stream_data)
append(&all_readers, ..other.readers[:])
} else {
append(&all_readers, w);
append(&all_readers, w)
}
}
mr.readers = all_readers;
mr.readers = all_readers
r.stream_vtable = _multi_reader_vtable;
r.stream_data = mr;
return;
r.stream_vtable = _multi_reader_vtable
r.stream_data = mr
return
}
multi_reader_destroy :: proc(mr: ^Multi_Reader) {
delete(mr.readers);
delete(mr.readers)
}
@@ -57,39 +57,39 @@ Multi_Writer :: struct {
@(private)
_multi_writer_vtable := &Stream_VTable{
impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
mw := (^Multi_Writer)(s.stream_data);
mw := (^Multi_Writer)(s.stream_data)
for w in mw.writers {
n, err = write(w, p);
n, err = write(w, p)
if err != nil {
return;
return
}
if n != len(p) {
err = .Short_Write;
return;
err = .Short_Write
return
}
}
return len(p), nil;
return len(p), nil
},
};
}
multi_writer_init :: proc(mw: ^Multi_Writer, writers: ..Writer, allocator := context.allocator) -> (out: Writer) {
mw.writers = make([dynamic]Writer, 0, len(writers), allocator);
mw.writers = make([dynamic]Writer, 0, len(writers), allocator)
for w in writers {
if w.stream_vtable == _multi_writer_vtable {
other := (^Multi_Writer)(w.stream_data);
append(&mw.writers, ..other.writers[:]);
other := (^Multi_Writer)(w.stream_data)
append(&mw.writers, ..other.writers[:])
} else {
append(&mw.writers, w);
append(&mw.writers, w)
}
}
out.stream_vtable = _multi_writer_vtable;
out.stream_data = mw;
return;
out.stream_vtable = _multi_writer_vtable
out.stream_data = mw
return
}
multi_writer_destroy :: proc(mw: ^Multi_Writer) {
delete(mw.writers);
delete(mw.writers)
}
+72 -72
View File
@@ -5,37 +5,37 @@ import "core:strconv"
read_ptr :: proc(r: Reader, p: rawptr, byte_size: int) -> (n: int, err: Error) {
return read(r, mem.byte_slice(p, byte_size));
return read(r, mem.byte_slice(p, byte_size))
}
write_ptr :: proc(w: Writer, p: rawptr, byte_size: int) -> (n: int, err: Error) {
return write(w, mem.byte_slice(p, byte_size));
return write(w, mem.byte_slice(p, byte_size))
}
read_ptr_at :: proc(r: Reader_At, p: rawptr, byte_size: int, offset: i64) -> (n: int, err: Error) {
return read_at(r, mem.byte_slice(p, byte_size), offset);
return read_at(r, mem.byte_slice(p, byte_size), offset)
}
write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64) -> (n: int, err: Error) {
return write_at(w, mem.byte_slice(p, byte_size), offset);
return write_at(w, mem.byte_slice(p, byte_size), offset)
}
write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) {
buf: [32]byte;
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil);
return write_string(w, s);
buf: [32]byte
s := strconv.append_bits(buf[:], 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);
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);
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);
return write_i64(w, i64(i), base)
}
Tee_Reader :: struct {
@@ -46,16 +46,16 @@ Tee_Reader :: struct {
@(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);
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 wn, werr
}
}
return;
return
},
};
}
// tee_reader_init 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'
@@ -64,14 +64,14 @@ _tee_reader_vtable := &Stream_VTable{
// Any error encountered whilst writing is reported as a 'read' error
// tee_reader_init must call io.destroy when done with
tee_reader_init :: proc(t: ^Tee_Reader, r: Reader, w: Writer, allocator := context.allocator) -> Reader {
t.r, t.w = r, w;
return tee_reader_to_reader(t);
t.r, t.w = r, w
return tee_reader_to_reader(t)
}
tee_reader_to_reader :: proc(t: ^Tee_Reader) -> (r: Reader) {
r.stream_data = t;
r.stream_vtable = _tee_reader_vtable;
return;
r.stream_data = t
r.stream_vtable = _tee_reader_vtable
return
}
@@ -86,30 +86,30 @@ Limited_Reader :: struct {
@(private)
_limited_reader_vtable := &Stream_VTable{
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
l := (^Limited_Reader)(s.stream_data);
l := (^Limited_Reader)(s.stream_data)
if l.n <= 0 {
return 0, .EOF;
return 0, .EOF
}
p := p;
p := p
if i64(len(p)) > l.n {
p = p[0:l.n];
p = p[0:l.n]
}
n, err = read(l.r, p);
l.n -= i64(n);
return;
n, err = read(l.r, p)
l.n -= i64(n)
return
},
};
}
limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
l.r = r;
l.n = n;
return limited_reader_to_reader(l);
l.r = r
l.n = n
return limited_reader_to_reader(l)
}
limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) {
r.stream_vtable = _limited_reader_vtable;
r.stream_data = l;
return;
r.stream_vtable = _limited_reader_vtable
r.stream_data = l
return
}
// Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At
@@ -121,74 +121,74 @@ Section_Reader :: struct {
}
section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) {
s.r = r;
s.off = off;
s.limit = off + n;
return;
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;
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);
s := (^Section_Reader)(stream.stream_data)
if s.off >= s.limit {
return 0, .EOF;
return 0, .EOF
}
p := p;
p := p
if max := s.limit - s.off; i64(len(p)) > max {
p = p[0:max];
p = p[0:max]
}
n, err = read_at(s.r, p, s.off);
s.off += i64(n);
return;
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;
s := (^Section_Reader)(stream.stream_data)
p, off := p, off
if off < 0 || off >= s.limit - s.base {
return 0, .EOF;
return 0, .EOF
}
off += s.base;
off += s.base
if max := s.limit - off; i64(len(p)) > max {
p = p[0:max];
n, err = read_at(s.r, p, off);
p = p[0:max]
n, err = read_at(s.r, p, off)
if err == nil {
err = .EOF;
err = .EOF
}
return;
return
}
return read_at(s.r, p, off);
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);
s := (^Section_Reader)(stream.stream_data)
offset := offset;
offset := offset
switch whence {
case:
return 0, .Invalid_Whence;
return 0, .Invalid_Whence
case .Start:
offset += s.base;
offset += s.base
case .Current:
offset += s.off;
offset += s.off
case .End:
offset += s.limit;
offset += s.limit
}
if offset < s.base {
return 0, .Invalid_Offset;
return 0, .Invalid_Offset
}
s.off = offset;
n = offset - s.base;
return;
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;
s := (^Section_Reader)(stream.stream_data)
return s.limit - s.base
},
};
}
+60 -60
View File
@@ -1,9 +1,9 @@
package log
import "core:fmt";
import "core:strings";
import "core:os";
import "core:time";
import "core:fmt"
import "core:strings"
import "core:os"
import "core:time"
Level_Headers := [?]string{
0..<10 = "[DEBUG] --- ",
@@ -11,7 +11,7 @@ Level_Headers := [?]string{
20..<30 = "[WARN ] --- ",
30..<40 = "[ERROR] --- ",
40..<50 = "[FATAL] --- ",
};
}
Default_Console_Logger_Opts :: Options{
.Level,
@@ -19,14 +19,14 @@ Default_Console_Logger_Opts :: Options{
.Short_File_Path,
.Line,
.Procedure,
} | Full_Timestamp_Opts;
} | Full_Timestamp_Opts
Default_File_Logger_Opts :: Options{
.Level,
.Short_File_Path,
.Line,
.Procedure,
} | Full_Timestamp_Opts;
} | Full_Timestamp_Opts
File_Console_Logger_Data :: struct {
@@ -35,128 +35,128 @@ File_Console_Logger_Data :: struct {
}
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
data := new(File_Console_Logger_Data);
data.file_handle = h;
data.ident = ident;
return Logger{file_console_logger_proc, data, lowest, opt};
data := new(File_Console_Logger_Data)
data.file_handle = h
data.ident = ident
return Logger{file_console_logger_proc, data, lowest, opt}
}
destroy_file_logger :: proc(log: ^Logger) {
data := cast(^File_Console_Logger_Data)log.data;
data := cast(^File_Console_Logger_Data)log.data
if data.file_handle != os.INVALID_HANDLE {
os.close(data.file_handle);
os.close(data.file_handle)
}
free(data);
free(data)
}
create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
data := new(File_Console_Logger_Data);
data.file_handle = os.INVALID_HANDLE;
data.ident = ident;
return Logger{file_console_logger_proc, data, lowest, opt};
data := new(File_Console_Logger_Data)
data.file_handle = os.INVALID_HANDLE
data.ident = ident
return Logger{file_console_logger_proc, data, lowest, opt}
}
destroy_console_logger :: proc(log: ^Logger) {
free(log.data);
free(log.data)
}
file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
data := cast(^File_Console_Logger_Data)logger_data;
h: os.Handle = os.stdout if level <= Level.Error else os.stderr;
data := cast(^File_Console_Logger_Data)logger_data
h: os.Handle = os.stdout if level <= Level.Error else os.stderr
if data.file_handle != os.INVALID_HANDLE {
h = data.file_handle;
h = data.file_handle
}
backing: [1024]byte; //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
buf := strings.builder_from_slice(backing[:]);
backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
buf := strings.builder_from_slice(backing[:])
do_level_header(options, level, &buf);
do_level_header(options, level, &buf)
when time.IS_SUPPORTED {
if Full_Timestamp_Opts & options != nil {
fmt.sbprint(&buf, "[");
t := time.now();
y, m, d := time.date(t);
h, min, s := time.clock(t);
if .Date in options { fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d); }
if .Time in options { fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s); }
fmt.sbprint(&buf, "] ");
fmt.sbprint(&buf, "[")
t := time.now()
y, m, d := time.date(t)
h, min, s := time.clock(t)
if .Date in options { fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d) }
if .Time in options { fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s) }
fmt.sbprint(&buf, "] ")
}
}
do_location_header(options, &buf, location);
do_location_header(options, &buf, location)
if .Thread_Id in options {
// NOTE(Oskar): not using context.thread_id here since that could be
// incorrect when replacing context for a thread.
fmt.sbprintf(&buf, "[{}] ", os.current_thread_id());
fmt.sbprintf(&buf, "[{}] ", os.current_thread_id())
}
if data.ident != "" {
fmt.sbprintf(&buf, "[%s] ", data.ident);
fmt.sbprintf(&buf, "[%s] ", data.ident)
}
//TODO(Hoej): When we have better atomics and such, make this thread-safe
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text);
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text)
}
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
RESET :: "\x1b[0m";
RED :: "\x1b[31m";
YELLOW :: "\x1b[33m";
DARK_GREY :: "\x1b[90m";
RESET :: "\x1b[0m"
RED :: "\x1b[31m"
YELLOW :: "\x1b[33m"
DARK_GREY :: "\x1b[90m"
col := RESET;
col := RESET
switch level {
case .Debug: col = DARK_GREY;
case .Info: col = RESET;
case .Warning: col = YELLOW;
case .Error, .Fatal: col = RED;
case .Debug: col = DARK_GREY
case .Info: col = RESET
case .Warning: col = YELLOW
case .Error, .Fatal: col = RED
}
if .Level in opts {
if .Terminal_Color in opts {
fmt.sbprint(str, col);
fmt.sbprint(str, col)
}
fmt.sbprint(str, Level_Headers[level]);
fmt.sbprint(str, Level_Headers[level])
if .Terminal_Color in opts {
fmt.sbprint(str, RESET);
fmt.sbprint(str, RESET)
}
}
}
do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #caller_location) {
if Location_Header_Opts & opts == nil {
return;
return
}
fmt.sbprint(buf, "[");
fmt.sbprint(buf, "[")
file := location.file_path;
file := location.file_path
if .Short_File_Path in opts {
last := 0;
last := 0
for r, i in location.file_path {
if r == '/' {
last = i+1;
last = i+1
}
}
file = location.file_path[last:];
file = location.file_path[last:]
}
if Location_File_Opts & opts != nil {
fmt.sbprint(buf, file);
fmt.sbprint(buf, file)
}
if .Line in opts {
if Location_File_Opts & opts != nil {
fmt.sbprint(buf, ":");
fmt.sbprint(buf, ":")
}
fmt.sbprint(buf, location.line);
fmt.sbprint(buf, location.line)
}
if .Procedure in opts {
if (Location_File_Opts | {.Line}) & opts != nil {
fmt.sbprint(buf, ":");
fmt.sbprint(buf, ":")
}
fmt.sbprintf(buf, "%s()", location.procedure);
fmt.sbprintf(buf, "%s()", location.procedure)
}
fmt.sbprint(buf, "] ");
fmt.sbprint(buf, "] ")
}
+33 -33
View File
@@ -6,7 +6,7 @@ import "core:fmt"
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
Level :: runtime.Logger_Level;
Level :: runtime.Logger_Level
/*
Logger_Level :: enum {
Debug = 0,
@@ -17,7 +17,7 @@ Logger_Level :: enum {
}
*/
Option :: runtime.Logger_Option;
Option :: runtime.Logger_Option
/*
Option :: enum {
Level,
@@ -31,7 +31,7 @@ Option :: enum {
}
*/
Options :: runtime.Logger_Options;
Options :: runtime.Logger_Options
/*
Options :: bit_set[Option];
*/
@@ -39,25 +39,25 @@ Options :: bit_set[Option];
Full_Timestamp_Opts :: Options{
.Date,
.Time,
};
}
Location_Header_Opts :: Options{
.Short_File_Path,
.Long_File_Path,
.Line,
.Procedure,
};
}
Location_File_Opts :: Options{
.Short_File_Path,
.Long_File_Path,
};
}
Logger_Proc :: runtime.Logger_Proc;
Logger_Proc :: runtime.Logger_Proc
/*
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
*/
Logger :: runtime.Logger;
Logger :: runtime.Logger
/*
Logger :: struct {
procedure: Logger_Proc,
@@ -72,74 +72,74 @@ nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Optio
}
nil_logger :: proc() -> Logger {
return Logger{nil_logger_proc, nil, Level.Debug, nil};
return Logger{nil_logger_proc, nil, Level.Debug, nil}
}
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
logf(level=.Debug, fmt_str=fmt_str, args=args, location=location);
logf(level=.Debug, fmt_str=fmt_str, args=args, location=location)
}
infof :: proc(fmt_str: string, args: ..any, location := #caller_location) {
logf(level=.Info, fmt_str=fmt_str, args=args, location=location);
logf(level=.Info, fmt_str=fmt_str, args=args, location=location)
}
warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
logf(level=.Warning, fmt_str=fmt_str, args=args, location=location);
logf(level=.Warning, fmt_str=fmt_str, args=args, location=location)
}
errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
logf(level=.Error, fmt_str=fmt_str, args=args, location=location);
logf(level=.Error, fmt_str=fmt_str, args=args, location=location)
}
fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location);
logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location)
}
debug :: proc(args: ..any, sep := " ", location := #caller_location) {
log(level=.Debug, args=args, sep=sep, location=location);
log(level=.Debug, args=args, sep=sep, location=location)
}
info :: proc(args: ..any, sep := " ", location := #caller_location) {
log(level=.Info, args=args, sep=sep, location=location);
log(level=.Info, args=args, sep=sep, location=location)
}
warn :: proc(args: ..any, sep := " ", location := #caller_location) {
log(level=.Warning, args=args, sep=sep, location=location);
log(level=.Warning, args=args, sep=sep, location=location)
}
error :: proc(args: ..any, sep := " ", location := #caller_location) {
log(level=.Error, args=args, sep=sep, location=location);
log(level=.Error, args=args, sep=sep, location=location)
}
fatal :: proc(args: ..any, sep := " ", location := #caller_location) {
log(level=.Fatal, args=args, sep=sep, location=location);
log(level=.Fatal, args=args, sep=sep, location=location)
}
panic :: proc(args: ..any, location := #caller_location) -> ! {
log(level=.Fatal, args=args, location=location);
runtime.panic("log.panic", location);
log(level=.Fatal, args=args, location=location)
runtime.panic("log.panic", location)
}
panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! {
logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location);
runtime.panic("log.panicf", location);
logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location)
runtime.panic("log.panicf", location)
}
log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) {
logger := context.logger;
logger := context.logger
if logger.procedure == nil {
return;
return
}
if level < logger.lowest_level {
return;
return
}
str := fmt.tprint(args=args, sep=sep); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
logger.procedure(logger.data, level, str, logger.options, location);
str := fmt.tprint(args=args, sep=sep) //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
logger.procedure(logger.data, level, str, logger.options, location)
}
logf :: proc(level: Level, fmt_str: string, args: ..any, location := #caller_location) {
logger := context.logger;
logger := context.logger
if logger.procedure == nil {
return;
return
}
if level < logger.lowest_level {
return;
return
}
str := fmt.tprintf(fmt_str, ..args);
logger.procedure(logger.data, level, str, logger.options, location);
str := fmt.tprintf(fmt_str, ..args)
logger.procedure(logger.data, level, str, logger.options, location)
}
+9 -9
View File
@@ -6,24 +6,24 @@ Multi_Logger_Data :: struct {
}
create_multi_logger :: proc(logs: ..Logger) -> Logger {
data := new(Multi_Logger_Data);
data.loggers = make([]Logger, len(logs));
copy(data.loggers, logs);
return Logger{multi_logger_proc, data, Level.Debug, nil};
data := new(Multi_Logger_Data)
data.loggers = make([]Logger, len(logs))
copy(data.loggers, logs)
return Logger{multi_logger_proc, data, Level.Debug, nil}
}
destroy_multi_logger :: proc(log : ^Logger) {
free(log.data);
log^ = nil_logger();
free(log.data)
log^ = nil_logger()
}
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
options: Options, location := #caller_location) {
data := cast(^Multi_Logger_Data)logger_data;
data := cast(^Multi_Logger_Data)logger_data
for log in data.loggers {
if level < log.lowest_level {
return;
return
}
log.procedure(log.data, level, text, log.options, location);
log.procedure(log.data, level, text, log.options, location)
}
}
+16 -16
View File
@@ -32,7 +32,7 @@ add :: proc {
int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error)
*/
int_add_digit,
};
}
/*
err = sub(dest, a, b);
@@ -46,7 +46,7 @@ sub :: proc {
int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error)
*/
int_sub_digit,
};
}
/*
=== === === === === === === === === === === === === === === === === === === === === === === ===
@@ -60,44 +60,44 @@ is_initialized :: proc {
int_is_initialized :: proc(a: ^Int) -> bool
*/
int_is_initialized,
};
}
is_zero :: proc {
/*
int_is_zero :: proc(a: ^Int) -> bool
*/
int_is_zero,
};
}
is_positive :: proc {
/*
int_is_positive :: proc(a: ^Int) -> bool
*/
int_is_positive,
};
is_pos :: is_positive;
}
is_pos :: is_positive
is_negative :: proc {
/*
int_is_negative :: proc(a: ^Int) -> bool
*/
int_is_negative,
};
is_neg :: is_negative;
}
is_neg :: is_negative
is_even :: proc {
/*
int_is_even :: proc(a: ^Int) -> bool
*/
int_is_even,
};
}
is_odd :: proc {
/*
int_is_odd :: proc(a: ^Int) -> bool
*/
int_is_odd,
};
}
is_power_of_two :: proc {
/*
@@ -108,7 +108,7 @@ is_power_of_two :: proc {
int_is_power_of_two :: proc(a: ^Int) -> (res: bool)
*/
int_is_power_of_two,
};
}
compare :: proc {
/*
@@ -123,16 +123,16 @@ compare :: proc {
int_compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag
*/
int_compare_digit,
};
cmp :: compare;
}
cmp :: compare
compare_magnitude :: proc {
/*
Compare the magnitude of two `Int`s, unsigned.
*/
int_compare_magnitude,
};
cmp_mag :: compare_magnitude;
}
cmp_mag :: compare_magnitude
/*
=== === === === === === === === === === === === === === === === === === === === === === === ===
@@ -148,6 +148,6 @@ destroy :: proc {
int_destroy :: proc(integers: ..^Int)
*/
int_destroy,
};
}
+58 -58
View File
@@ -32,15 +32,15 @@ import "core:intrinsics"
To allow tests to run we add `-define:MATH_BIG_EXE=false` to hardcode the cutoffs for now.
*/
when #config(MATH_BIG_EXE, true) {
MUL_KARATSUBA_CUTOFF := initialize_constants();
SQR_KARATSUBA_CUTOFF := _DEFAULT_SQR_KARATSUBA_CUTOFF;
MUL_TOOM_CUTOFF := _DEFAULT_MUL_TOOM_CUTOFF;
SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF;
MUL_KARATSUBA_CUTOFF := initialize_constants()
SQR_KARATSUBA_CUTOFF := _DEFAULT_SQR_KARATSUBA_CUTOFF
MUL_TOOM_CUTOFF := _DEFAULT_MUL_TOOM_CUTOFF
SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF
} else {
MUL_KARATSUBA_CUTOFF := _DEFAULT_MUL_KARATSUBA_CUTOFF;
SQR_KARATSUBA_CUTOFF := _DEFAULT_SQR_KARATSUBA_CUTOFF;
MUL_TOOM_CUTOFF := _DEFAULT_MUL_TOOM_CUTOFF;
SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF;
MUL_KARATSUBA_CUTOFF := _DEFAULT_MUL_KARATSUBA_CUTOFF
SQR_KARATSUBA_CUTOFF := _DEFAULT_SQR_KARATSUBA_CUTOFF
MUL_TOOM_CUTOFF := _DEFAULT_MUL_TOOM_CUTOFF
SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF
}
/*
@@ -56,48 +56,48 @@ when #config(MATH_BIG_EXE, true) {
debugged where necessary.
*/
_DEFAULT_MUL_KARATSUBA_CUTOFF :: #config(MATH_BIG_MUL_KARATSUBA_CUTOFF, 80);
_DEFAULT_SQR_KARATSUBA_CUTOFF :: #config(MATH_BIG_SQR_KARATSUBA_CUTOFF, 120);
_DEFAULT_MUL_TOOM_CUTOFF :: #config(MATH_BIG_MUL_TOOM_CUTOFF, 350);
_DEFAULT_SQR_TOOM_CUTOFF :: #config(MATH_BIG_SQR_TOOM_CUTOFF, 400);
_DEFAULT_MUL_KARATSUBA_CUTOFF :: #config(MATH_BIG_MUL_KARATSUBA_CUTOFF, 80)
_DEFAULT_SQR_KARATSUBA_CUTOFF :: #config(MATH_BIG_SQR_KARATSUBA_CUTOFF, 120)
_DEFAULT_MUL_TOOM_CUTOFF :: #config(MATH_BIG_MUL_TOOM_CUTOFF, 350)
_DEFAULT_SQR_TOOM_CUTOFF :: #config(MATH_BIG_SQR_TOOM_CUTOFF, 400)
MAX_ITERATIONS_ROOT_N := 500;
MAX_ITERATIONS_ROOT_N := 500
/*
Largest `N` for which we'll compute `N!`
*/
FACTORIAL_MAX_N := 1_000_000;
FACTORIAL_MAX_N := 1_000_000
/*
Cutoff to switch to int_factorial_binary_split, and its max recursion level.
*/
FACTORIAL_BINARY_SPLIT_CUTOFF := 6100;
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS := 100;
FACTORIAL_BINARY_SPLIT_CUTOFF := 6100
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS := 100
/*
`internal_int_is_prime` switchables.
Use Frobenius-Underwood for primality testing, or use Lucas-Selfridge (default).
*/
MATH_BIG_USE_LUCAS_SELFRIDGE_TEST :: #config(MATH_BIG_USE_LUCAS_SELFRIDGE_TEST, false);
MATH_BIG_USE_FROBENIUS_TEST :: !MATH_BIG_USE_LUCAS_SELFRIDGE_TEST;
MATH_BIG_USE_LUCAS_SELFRIDGE_TEST :: #config(MATH_BIG_USE_LUCAS_SELFRIDGE_TEST, false)
MATH_BIG_USE_FROBENIUS_TEST :: !MATH_BIG_USE_LUCAS_SELFRIDGE_TEST
/*
Runtime tunable to use Miller-Rabin primality testing only and skip the above.
*/
USE_MILLER_RABIN_ONLY := false;
USE_MILLER_RABIN_ONLY := false
/*
How many times we'll call `internal_int_random` during random prime generation before we bail out.
Set to 0 or less to try indefinitely.
*/
MAX_ITERATIONS_RANDOM_PRIME := 1_000_000;
MAX_ITERATIONS_RANDOM_PRIME := 1_000_000
/*
How many iterations we used for the last random prime.
*/
@thread_local RANDOM_PRIME_ITERATIONS_USED: int;
@thread_local RANDOM_PRIME_ITERATIONS_USED: int
/*
We don't allow these to be switched at runtime for two reasons:
@@ -107,22 +107,22 @@ MAX_ITERATIONS_RANDOM_PRIME := 1_000_000;
2) Optimizations thanks to precomputed masks wouldn't work.
*/
MATH_BIG_FORCE_64_BIT :: #config(MATH_BIG_FORCE_64_BIT, false);
MATH_BIG_FORCE_32_BIT :: #config(MATH_BIG_FORCE_32_BIT, false);
when (MATH_BIG_FORCE_32_BIT && MATH_BIG_FORCE_64_BIT) { #panic("Cannot force 32-bit and 64-bit big backend simultaneously."); };
MATH_BIG_FORCE_64_BIT :: #config(MATH_BIG_FORCE_64_BIT, false)
MATH_BIG_FORCE_32_BIT :: #config(MATH_BIG_FORCE_32_BIT, false)
when (MATH_BIG_FORCE_32_BIT && MATH_BIG_FORCE_64_BIT) { #panic("Cannot force 32-bit and 64-bit big backend simultaneously.") }
/*
Trade a smaller memory footprint for more processing overhead?
*/
_LOW_MEMORY :: #config(MATH_BIG_SMALL_MEMORY, false);
_LOW_MEMORY :: #config(MATH_BIG_SMALL_MEMORY, false)
when _LOW_MEMORY {
_DEFAULT_DIGIT_COUNT :: 8;
_TAB_SIZE :: 32;
_MAX_WIN_SIZE :: 5;
_DEFAULT_DIGIT_COUNT :: 8
_TAB_SIZE :: 32
_MAX_WIN_SIZE :: 5
} else {
_DEFAULT_DIGIT_COUNT :: 32;
_TAB_SIZE :: 256;
_MAX_WIN_SIZE :: 0;
_DEFAULT_DIGIT_COUNT :: 32
_TAB_SIZE :: 256
_MAX_WIN_SIZE :: 0
}
/*
@@ -132,22 +132,22 @@ when _LOW_MEMORY {
Sign :: enum u8 {
Zero_or_Positive = 0,
Negative = 1,
};
}
Int :: struct {
used: int,
digit: [dynamic]DIGIT,
sign: Sign,
flags: Flags,
};
}
Flag :: enum u8 {
NaN,
Inf,
Immutable,
};
}
Flags :: bit_set[Flag; u8];
Flags :: bit_set[Flag; u8]
/*
Errors are a strict superset of runtime.Allocation_Error.
@@ -171,7 +171,7 @@ Error :: enum int {
Cannot_Write_File = 52,
Unimplemented = 127,
};
}
Error_String :: #partial [Error]string{
.Out_Of_Memory = "Out of memory",
@@ -191,14 +191,14 @@ Error_String :: #partial [Error]string{
.Cannot_Write_File = "Cannot_Write_File",
.Unimplemented = "Unimplemented",
};
}
Primality_Flag :: enum u8 {
Blum_Blum_Shub = 0, // Make prime congruent to 3 mod 4
Safe = 1, // Make sure (p-1)/2 is prime as well (implies .Blum_Blum_Shub)
Second_MSB_On = 3, // Make the 2nd highest bit one
};
Primality_Flags :: bit_set[Primality_Flag; u8];
}
Primality_Flags :: bit_set[Primality_Flag; u8]
/*
How do we store the Ints?
@@ -208,8 +208,8 @@ Primality_Flags :: bit_set[Primality_Flag; u8];
- Must be large enough such that `init_integer` can store `u128` in the `Int` without growing.
*/
_MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS);
#assert(_DEFAULT_DIGIT_COUNT >= _MIN_DIGIT_COUNT);
_MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS)
#assert(_DEFAULT_DIGIT_COUNT >= _MIN_DIGIT_COUNT)
/*
Maximum number of digits.
@@ -217,37 +217,37 @@ _MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS);
- Must be small enough such that `_radix_size` for base 2 does not overflow.
`_radix_size` needs two additional bytes for zero termination and sign.
*/
_MAX_BIT_COUNT :: (max(int) - 2);
_MAX_DIGIT_COUNT :: _MAX_BIT_COUNT / _DIGIT_BITS;
_MAX_BIT_COUNT :: (max(int) - 2)
_MAX_DIGIT_COUNT :: _MAX_BIT_COUNT / _DIGIT_BITS
when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) {
/*
We can use u128 as an intermediary.
*/
DIGIT :: distinct u64;
_WORD :: distinct u128;
DIGIT :: distinct u64
_WORD :: distinct u128
} else {
DIGIT :: distinct u32;
_WORD :: distinct u64;
DIGIT :: distinct u32
_WORD :: distinct u64
}
#assert(size_of(_WORD) == 2 * size_of(DIGIT));
#assert(size_of(_WORD) == 2 * size_of(DIGIT))
_DIGIT_TYPE_BITS :: 8 * size_of(DIGIT);
_WORD_TYPE_BITS :: 8 * size_of(_WORD);
_DIGIT_TYPE_BITS :: 8 * size_of(DIGIT)
_WORD_TYPE_BITS :: 8 * size_of(_WORD)
_DIGIT_NAILS :: 4;
_DIGIT_BITS :: _DIGIT_TYPE_BITS - _DIGIT_NAILS;
_WORD_BITS :: 2 * _DIGIT_BITS;
_DIGIT_NAILS :: 4
_DIGIT_BITS :: _DIGIT_TYPE_BITS - _DIGIT_NAILS
_WORD_BITS :: 2 * _DIGIT_BITS
_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1);
_DIGIT_MAX :: _MASK;
_MAX_COMBA :: 1 << (_WORD_TYPE_BITS - (2 * _DIGIT_BITS)) ;
_WARRAY :: 1 << ((_WORD_TYPE_BITS - (2 * _DIGIT_BITS)) + 1);
_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1)
_DIGIT_MAX :: _MASK
_MAX_COMBA :: 1 << (_WORD_TYPE_BITS - (2 * _DIGIT_BITS))
_WARRAY :: 1 << ((_WORD_TYPE_BITS - (2 * _DIGIT_BITS)) + 1)
Order :: enum i8 {
LSB_First = -1,
MSB_First = 1,
};
}
Endianness :: enum i8 {
Little = -1,
+44 -44
View File
@@ -60,93 +60,93 @@ FACTORIAL_BINARY_SPLIT_CUTOFF,
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS,
USE_MILLER_RABIN_ONLY,
MAX_ITERATIONS_RANDOM_PRIME,
);
)
}
print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) {
assert_if_nil(a);
assert_if_nil(a)
as, err := itoa(a, base);
defer delete(as);
as, err := itoa(a, base)
defer delete(as)
cb := internal_count_bits(a);
cb := internal_count_bits(a)
if print_name {
fmt.printf("%v", name);
fmt.printf("%v", name)
}
if err != nil {
fmt.printf("%v (error: %v | %v)", name, err, a);
fmt.printf("%v (error: %v | %v)", name, err, a)
}
fmt.printf("%v", as);
fmt.printf("%v", as)
if print_extra_info {
fmt.printf(" (base: %v, bits: %v (digits: %v), flags: %v)", base, cb, a.used, a.flags);
fmt.printf(" (base: %v, bits: %v (digits: %v), flags: %v)", base, cb, a.used, a.flags)
}
if newline {
fmt.println();
fmt.println()
}
}
// printf :: fmt.printf;
demo :: proc() {
a, b, c, d, e, f, res := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
defer destroy(a, b, c, d, e, f, res);
a, b, c, d, e, f, res := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}
defer destroy(a, b, c, d, e, f, res)
bits := 111;
trials := -1;
bits := 111
trials := -1
flags := Primality_Flags{};
fmt.printf("Trying to generate a %v bit prime using %v Miller-Rabin trials and options %v.\n", bits, trials, flags);
flags := Primality_Flags{}
fmt.printf("Trying to generate a %v bit prime using %v Miller-Rabin trials and options %v.\n", bits, trials, flags)
err: Error;
err: Error
{
SCOPED_TIMING(.random_prime);
err = internal_random_prime(a, bits, trials, flags);
SCOPED_TIMING(.random_prime)
err = internal_random_prime(a, bits, trials, flags)
}
print("a(10): ", a, 10, true, true, true);
fmt.printf("err: %v\n", err);
fmt.printf("RANDOM_PRIME_ITERATIONS_USED: %v\n", RANDOM_PRIME_ITERATIONS_USED);
print("a(10): ", a, 10, true, true, true)
fmt.printf("err: %v\n", err)
fmt.printf("RANDOM_PRIME_ITERATIONS_USED: %v\n", RANDOM_PRIME_ITERATIONS_USED)
nails := 0;
nails := 0
count := internal_int_pack_count(a, u8, nails);
buf := make([]u8, count);
defer delete(buf);
count := internal_int_pack_count(a, u8, nails)
buf := make([]u8, count)
defer delete(buf)
written: int;
order := Order.LSB_First;
written: int
order := Order.LSB_First
fmt.printf("\na.digit: %v\n", a.digit[:a.used]);
written, err = internal_int_pack(a, buf, nails, order);
fmt.printf("\nPacked into buf: %v | err: %v | written: %v\n", buf, err, written);
fmt.printf("\na.digit: %v\n", a.digit[:a.used])
written, err = internal_int_pack(a, buf, nails, order)
fmt.printf("\nPacked into buf: %v | err: %v | written: %v\n", buf, err, written)
err = internal_int_unpack(b, buf, nails, order);
print("\nUnpacked into b: ", b);
fmt.printf("err: %v\n", err);
fmt.printf("b.digit: %v\n", b.digit[:b.used]);
err = internal_int_unpack(b, buf, nails, order)
print("\nUnpacked into b: ", b)
fmt.printf("err: %v\n", err)
fmt.printf("b.digit: %v\n", b.digit[:b.used])
}
main :: proc() {
ta := mem.Tracking_Allocator{};
mem.tracking_allocator_init(&ta, context.allocator);
context.allocator = mem.tracking_allocator(&ta);
ta := mem.Tracking_Allocator{}
mem.tracking_allocator_init(&ta, context.allocator)
context.allocator = mem.tracking_allocator(&ta)
demo();
demo()
print_configation();
print_configation()
print_timings();
print_timings()
if len(ta.allocation_map) > 0 {
for _, v in ta.allocation_map {
fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location);
fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location)
}
}
if len(ta.bad_free_array) > 0 {
fmt.println("Bad frees:");
fmt.println("Bad frees:")
for v in ta.bad_free_array {
fmt.println(v);
fmt.println(v)
}
}
}
+287 -287
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+46 -46
View File
@@ -21,37 +21,37 @@ package math_big
2's complement `and`, returns `dest = a & b;`
*/
int_and :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, a, b);
context.allocator = allocator;
assert_if_nil(dest, a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_int_and(dest, a, b);
internal_clear_if_uninitialized(a, b) or_return
return #force_inline internal_int_and(dest, a, b)
}
and :: proc { int_and, };
and :: proc { int_and, }
/*
2's complement `or`, returns `dest = a | b;`
*/
int_or :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, a, b);
context.allocator = allocator;
assert_if_nil(dest, a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_int_or(dest, a, b);
internal_clear_if_uninitialized(a, b) or_return
return #force_inline internal_int_or(dest, a, b)
}
or :: proc { int_or, };
or :: proc { int_or, }
/*
2's complement `xor`, returns `dest = a ^ b;`
*/
int_xor :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, a, b);
context.allocator = allocator;
assert_if_nil(dest, a, b)
context.allocator = allocator
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_int_xor(dest, a, b);
internal_clear_if_uninitialized(a, b) or_return
return #force_inline internal_int_xor(dest, a, b)
}
xor :: proc { int_xor, };
xor :: proc { int_xor, }
/*
dest = ~src
@@ -60,31 +60,31 @@ int_complement :: proc(dest, src: ^Int, allocator := context.allocator) -> (err:
/*
Check that `src` and `dest` are usable.
*/
assert_if_nil(dest, src);
context.allocator = allocator;
assert_if_nil(dest, src)
context.allocator = allocator
internal_clear_if_uninitialized(dest, src) or_return;
return #force_inline internal_int_complement(dest, src);
internal_clear_if_uninitialized(dest, src) or_return
return #force_inline internal_int_complement(dest, src)
}
complement :: proc { int_complement, };
complement :: proc { int_complement, }
/*
quotient, remainder := numerator >> bits;
`remainder` is allowed to be passed a `nil`, in which case `mod` won't be computed.
*/
int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(quotient, numerator);
context.allocator = allocator;
assert_if_nil(quotient, numerator)
context.allocator = allocator
if err = internal_clear_if_uninitialized(quotient, numerator); err != nil { return err; }
return #force_inline internal_int_shrmod(quotient, remainder, numerator, bits);
if err = internal_clear_if_uninitialized(quotient, numerator); err != nil { return err }
return #force_inline internal_int_shrmod(quotient, remainder, numerator, bits)
}
shrmod :: proc { int_shrmod, };
shrmod :: proc { int_shrmod, }
int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
return #force_inline shrmod(dest, nil, source, bits, allocator);
return #force_inline shrmod(dest, nil, source, bits, allocator)
}
shr :: proc { int_shr, };
shr :: proc { int_shr, }
/*
Shift right by `digits` * _DIGIT_BITS bits.
@@ -93,38 +93,38 @@ int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocato
/*
Check that `quotient` is usable.
*/
assert_if_nil(quotient);
context.allocator = allocator;
assert_if_nil(quotient)
context.allocator = allocator
internal_clear_if_uninitialized(quotient) or_return;
return #force_inline internal_int_shr_digit(quotient, digits);
internal_clear_if_uninitialized(quotient) or_return
return #force_inline internal_int_shr_digit(quotient, digits)
}
shr_digit :: proc { int_shr_digit, };
shr_digit :: proc { int_shr_digit, }
/*
Shift right by a certain bit count with sign extension.
*/
int_shr_signed :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, src);
context.allocator = allocator;
assert_if_nil(dest, src)
context.allocator = allocator
internal_clear_if_uninitialized(dest, src) or_return;
return #force_inline internal_int_shr_signed(dest, src, bits);
internal_clear_if_uninitialized(dest, src) or_return
return #force_inline internal_int_shr_signed(dest, src, bits)
}
shr_signed :: proc { int_shr_signed, };
shr_signed :: proc { int_shr_signed, }
/*
Shift left by a certain bit count.
*/
int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, src);
context.allocator = allocator;
assert_if_nil(dest, src)
context.allocator = allocator
internal_clear_if_uninitialized(dest, src) or_return;
return #force_inline internal_int_shl(dest, src, bits);
internal_clear_if_uninitialized(dest, src) or_return
return #force_inline internal_int_shl(dest, src, bits)
}
shl :: proc { int_shl, };
shl :: proc { int_shl, }
/*
@@ -134,10 +134,10 @@ int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocato
/*
Check that `quotient` is usable.
*/
assert_if_nil(quotient);
context.allocator = allocator;
assert_if_nil(quotient)
context.allocator = allocator
internal_clear_if_uninitialized(quotient) or_return;
return #force_inline internal_int_shl_digit(quotient, digits);
internal_clear_if_uninitialized(quotient) or_return
return #force_inline internal_int_shl_digit(quotient, digits)
}
shl_digit :: proc { int_shl_digit, };
+363 -363
View File
File diff suppressed because it is too large Load Diff
+933 -933
View File
File diff suppressed because it is too large Load Diff
+302 -302
View File
File diff suppressed because it is too large Load Diff
+193 -193
View File
@@ -23,11 +23,11 @@ import "core:os"
The radix defaults to 10.
*/
int_itoa_string :: proc(a: ^Int, radix := i8(10), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
assert_if_nil(a)
context.allocator = allocator
a := a; radix := radix;
clear_if_uninitialized(a) or_return;
a := a; radix := radix
clear_if_uninitialized(a) or_return
/*
TODO: If we want to write a prefix for some of the radixes, we can oversize the buffer.
@@ -38,20 +38,20 @@ int_itoa_string :: proc(a: ^Int, radix := i8(10), zero_terminate := false, alloc
Calculate the size of the buffer we need, and
Exit if calculating the size returned an error.
*/
size := radix_size(a, radix, zero_terminate) or_return;
size := radix_size(a, radix, zero_terminate) or_return
/*
Allocate the buffer we need.
*/
buffer := make([]u8, size);
buffer := make([]u8, size)
/*
Write the digits out into the buffer.
*/
written: int;
written, err = int_itoa_raw(a, radix, buffer, size, zero_terminate);
written: int
written, err = int_itoa_raw(a, radix, buffer, size, zero_terminate)
return string(buffer[:written]), err;
return string(buffer[:written]), err
}
/*
@@ -59,15 +59,15 @@ int_itoa_string :: proc(a: ^Int, radix := i8(10), zero_terminate := false, alloc
The radix defaults to 10.
*/
int_itoa_cstring :: proc(a: ^Int, radix := i8(10), allocator := context.allocator) -> (res: cstring, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
assert_if_nil(a)
context.allocator = allocator
a := a; radix := radix;
clear_if_uninitialized(a) or_return;
a := a; radix := radix
clear_if_uninitialized(a) or_return
s: string;
s, err = int_itoa_string(a, radix, true);
return cstring(raw_data(s)), err;
s: string
s, err = int_itoa_string(a, radix, true)
return cstring(raw_data(s)), err
}
/*
@@ -91,57 +91,57 @@ int_itoa_cstring :: proc(a: ^Int, radix := i8(10), allocator := context.allocato
and having to perform a buffer overflow check each character.
*/
int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) {
assert_if_nil(a);
a := a; radix := radix; size := size;
clear_if_uninitialized(a) or_return;
assert_if_nil(a)
a := a; radix := radix; size := size
clear_if_uninitialized(a) or_return
/*
Radix defaults to 10.
*/
radix = radix if radix > 0 else 10;
radix = radix if radix > 0 else 10
if radix < 2 || radix > 64 {
return 0, .Invalid_Argument;
return 0, .Invalid_Argument
}
/*
We weren't given a size. Let's compute it.
*/
if size == -1 {
size = radix_size(a, radix, zero_terminate) or_return;
size = radix_size(a, radix, zero_terminate) or_return
}
/*
Early exit if the buffer we were given is too small.
*/
available := len(buffer);
available := len(buffer)
if available < size {
return 0, .Buffer_Overflow;
return 0, .Buffer_Overflow
}
/*
Fast path for when `Int` == 0 or the entire `Int` fits in a single radix digit.
*/
z, _ := is_zero(a);
z, _ := is_zero(a)
if z || (a.used == 1 && a.digit[0] < DIGIT(radix)) {
if zero_terminate {
available -= 1;
buffer[available] = 0;
available -= 1
buffer[available] = 0
}
available -= 1;
buffer[available] = RADIX_TABLE[a.digit[0]];
available -= 1
buffer[available] = RADIX_TABLE[a.digit[0]]
if n, _ := is_neg(a); n {
available -= 1;
buffer[available] = '-';
available -= 1
buffer[available] = '-'
}
/*
If we overestimated the size, we need to move the buffer left.
*/
written = len(buffer) - available;
written = len(buffer) - available
if written < size {
diff := size - written;
mem.copy(&buffer[0], &buffer[diff], written);
diff := size - written
mem.copy(&buffer[0], &buffer[diff], written)
}
return written, nil;
return written, nil
}
/*
@@ -149,32 +149,32 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter
*/
if a.used == 1 || a.used == 2 {
if zero_terminate {
available -= 1;
buffer[available] = 0;
available -= 1
buffer[available] = 0
}
val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]);
val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0])
for val > 0 {
q := val / _WORD(radix);
available -= 1;
buffer[available] = RADIX_TABLE[val - (q * _WORD(radix))];
q := val / _WORD(radix)
available -= 1
buffer[available] = RADIX_TABLE[val - (q * _WORD(radix))]
val = q;
val = q
}
if n, _ := is_neg(a); n {
available -= 1;
buffer[available] = '-';
available -= 1
buffer[available] = '-'
}
/*
If we overestimated the size, we need to move the buffer left.
*/
written = len(buffer) - available;
written = len(buffer) - available
if written < size {
diff := size - written;
mem.copy(&buffer[0], &buffer[diff], written);
diff := size - written
mem.copy(&buffer[0], &buffer[diff], written)
}
return written, nil;
return written, nil
}
/*
@@ -182,153 +182,153 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter
*/
if is_power_of_two(int(radix)) {
if zero_terminate {
available -= 1;
buffer[available] = 0;
available -= 1
buffer[available] = 0
}
shift, count: int;
shift, count: int
// mask := _WORD(radix - 1);
shift, err = log(DIGIT(radix), 2);
count, err = count_bits(a);
digit: _WORD;
shift, err = log(DIGIT(radix), 2)
count, err = count_bits(a)
digit: _WORD
for offset := 0; offset < count; offset += shift {
bits_to_get := int(min(count - offset, shift));
bits_to_get := int(min(count - offset, shift))
digit, err = int_bitfield_extract(a, offset, bits_to_get);
digit, err = int_bitfield_extract(a, offset, bits_to_get)
if err != nil {
return len(buffer) - available, .Invalid_Argument;
return len(buffer) - available, .Invalid_Argument
}
available -= 1;
buffer[available] = RADIX_TABLE[digit];
available -= 1
buffer[available] = RADIX_TABLE[digit]
}
if n, _ := is_neg(a); n {
available -= 1;
buffer[available] = '-';
available -= 1
buffer[available] = '-'
}
/*
If we overestimated the size, we need to move the buffer left.
*/
written = len(buffer) - available;
written = len(buffer) - available
if written < size {
diff := size - written;
mem.copy(&buffer[0], &buffer[diff], written);
diff := size - written
mem.copy(&buffer[0], &buffer[diff], written)
}
return written, nil;
return written, nil
}
return _itoa_raw_full(a, radix, buffer, zero_terminate);
return _itoa_raw_full(a, radix, buffer, zero_terminate)
}
itoa :: proc{int_itoa_string, int_itoa_raw};
int_to_string :: int_itoa_string;
int_to_cstring :: int_itoa_cstring;
itoa :: proc{int_itoa_string, int_itoa_raw}
int_to_string :: int_itoa_string
int_to_cstring :: int_itoa_cstring
/*
Read a string [ASCII] in a given radix.
*/
int_atoi :: proc(res: ^Int, input: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
assert_if_nil(res);
input := input;
context.allocator = allocator;
assert_if_nil(res)
input := input
context.allocator = allocator
/*
Make sure the radix is ok.
*/
if radix < 2 || radix > 64 { return .Invalid_Argument; }
if radix < 2 || radix > 64 { return .Invalid_Argument }
/*
Set the integer to the default of zero.
*/
internal_zero(res) or_return;
internal_zero(res) or_return
/*
We'll interpret an empty string as zero.
*/
if len(input) == 0 {
return nil;
return nil
}
/*
If the leading digit is a minus set the sign to negative.
Given the above early out, the length should be at least 1.
*/
sign := Sign.Zero_or_Positive;
sign := Sign.Zero_or_Positive
if input[0] == '-' {
input = input[1:];
sign = .Negative;
input = input[1:]
sign = .Negative
}
/*
Process each digit of the string.
*/
ch: rune;
ch: rune
for len(input) > 0 {
/* if the radix <= 36 the conversion is case insensitive
* this allows numbers like 1AB and 1ab to represent the same value
* [e.g. in hex]
*/
ch = rune(input[0]);
ch = rune(input[0])
if radix <= 36 && ch >= 'a' && ch <= 'z' {
ch -= 32; // 'a' - 'A'
ch -= 32 // 'a' - 'A'
}
pos := ch - '+';
pos := ch - '+'
if RADIX_TABLE_REVERSE_SIZE <= pos {
break;
break
}
y := RADIX_TABLE_REVERSE[pos];
y := RADIX_TABLE_REVERSE[pos]
/* if the char was found in the map
* and is less than the given radix add it
* to the number, otherwise exit the loop.
*/
if y >= u8(radix) {
break;
break
}
internal_mul(res, res, DIGIT(radix)) or_return;
internal_add(res, res, DIGIT(y)) or_return;
internal_mul(res, res, DIGIT(radix)) or_return
internal_add(res, res, DIGIT(y)) or_return
input = input[1:];
input = input[1:]
}
/*
If an illegal character was found, fail.
*/
if len(input) > 0 && ch != 0 && ch != '\r' && ch != '\n' {
return .Invalid_Argument;
return .Invalid_Argument
}
/*
Set the sign only if res != 0.
*/
if res.used > 0 {
res.sign = sign;
res.sign = sign
}
return nil;
return nil
}
atoi :: proc { int_atoi, };
atoi :: proc { int_atoi, }
/*
We size for `string` by default.
*/
radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false, allocator := context.allocator) -> (size: int, err: Error) {
a := a;
assert_if_nil(a);
a := a
assert_if_nil(a)
if radix < 2 || radix > 64 { return -1, .Invalid_Argument; }
clear_if_uninitialized(a) or_return;
if radix < 2 || radix > 64 { return -1, .Invalid_Argument }
clear_if_uninitialized(a) or_return
if internal_is_zero(a) {
if zero_terminate {
return 2, nil;
return 2, nil
}
return 1, nil;
return 1, nil
}
if internal_is_power_of_two(a) {
@@ -339,37 +339,37 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false, allocator := con
used = a.used,
sign = .Zero_or_Positive,
digit = a.digit,
};
}
size = internal_log(t, DIGIT(radix)) or_return;
size = internal_log(t, DIGIT(radix)) or_return
} else {
la, k := &Int{}, &Int{};
defer internal_destroy(la, k);
la, k := &Int{}, &Int{}
defer internal_destroy(la, k)
/* la = floor(log_2(a)) + 1 */
bit_count := internal_count_bits(a);
internal_set(la, bit_count) or_return;
bit_count := internal_count_bits(a)
internal_set(la, bit_count) or_return
/* k = floor(2^29/log_2(radix)) + 1 */
lb := _log_bases;
internal_set(k, lb[radix]) or_return;
lb := _log_bases
internal_set(k, lb[radix]) or_return
/* n = floor((la * k) / 2^29) + 1 */
internal_mul(k, la, k) or_return;
internal_shr(k, k, _RADIX_SIZE_SCALE) or_return;
internal_mul(k, la, k) or_return
internal_shr(k, k, _RADIX_SIZE_SCALE) or_return
/* The "+1" here is the "+1" in "floor((la * k) / 2^29) + 1" */
/* n = n + 1 + EOS + sign */
size_, _ := internal_get(k, u128);
size = int(size_);
size_, _ := internal_get(k, u128)
size = int(size_)
}
/*
log truncates to zero, so we need to add one more, and one for `-` if negative.
*/
size += 2 if a.sign == .Negative else 1;
size += 1 if zero_terminate else 0;
return size, nil;
size += 2 if a.sign == .Negative else 1
size += 1 if zero_terminate else 0
return size, nil
}
/*
@@ -386,7 +386,7 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false, allocator := con
Read an Int from an ASCII file.
*/
internal_int_read_from_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
context.allocator = allocator;
context.allocator = allocator
/*
We can either read the entire file at once, or read a bunch at a time and keep multiplying by the radix.
@@ -394,41 +394,41 @@ internal_int_read_from_ascii_file :: proc(a: ^Int, filename: string, radix := i8
of `atoi` so we don't need to read the entire file.
*/
res, ok := os.read_entire_file(filename, allocator);
defer delete(res, allocator);
res, ok := os.read_entire_file(filename, allocator)
defer delete(res, allocator)
if !ok {
return .Cannot_Read_File;
return .Cannot_Read_File
}
as := string(res);
return atoi(a, as, radix);
as := string(res)
return atoi(a, as, radix)
}
/*
Write an Int to an ASCII file.
*/
internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
context.allocator = allocator;
context.allocator = allocator
/*
For now we'll convert the Int using itoa and writing the result in one go.
If we want to preserve memory we could duplicate the itoa logic and write backwards.
*/
as := itoa(a, radix) or_return;
defer delete(as);
as := itoa(a, radix) or_return
defer delete(as)
l := len(as);
assert(l > 0);
l := len(as)
assert(l > 0)
data := transmute([]u8)mem.Raw_Slice{
data = raw_data(as),
len = l,
};
}
ok := os.write_entire_file(name=filename, data=data, truncate=true);
return nil if ok else .Cannot_Write_File;
ok := os.write_entire_file(name=filename, data=data, truncate=true)
return nil if ok else .Cannot_Write_File
}
/*
@@ -437,15 +437,15 @@ internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8(
See https://gmplib.org/manual/Integer-Import-and-Export.html
*/
internal_int_pack_count :: proc(a: ^Int, $T: typeid, nails := 0) -> (size_needed: int) {
assert(nails >= 0 && nails < (size_of(T) * 8));
assert(nails >= 0 && nails < (size_of(T) * 8))
bits := internal_count_bits(a);
size := size_of(T);
bits := internal_count_bits(a)
size := size_of(T)
size_needed = bits / ((size * 8) - nails);
size_needed += 1 if (bits % ((size * 8) - nails)) != 0 else 0;
size_needed = bits / ((size * 8) - nails)
size_needed += 1 if (bits % ((size * 8) - nails)) != 0 else 0
return size_needed;
return size_needed
}
/*
@@ -469,78 +469,78 @@ internal_int_pack_count :: proc(a: ^Int, $T: typeid, nails := 0) -> (size_needed
internal_int_pack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First) -> (written: int, err: Error)
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
assert(nails >= 0 && nails < (size_of(T) * 8));
assert(nails >= 0 && nails < (size_of(T) * 8))
type_size := size_of(T);
type_bits := (type_size * 8) - nails;
type_size := size_of(T)
type_bits := (type_size * 8) - nails
word_count := internal_int_pack_count(a, T, nails);
bit_count := internal_count_bits(a);
word_count := internal_int_pack_count(a, T, nails)
bit_count := internal_count_bits(a)
if len(buf) < word_count {
return 0, .Buffer_Overflow;
return 0, .Buffer_Overflow
}
bit_offset := 0;
word_offset := 0;
bit_offset := 0
word_offset := 0
#no_bounds_check for i := 0; i < word_count; i += 1 {
bit_offset = i * type_bits;
bit_offset = i * type_bits
if order == .MSB_First {
word_offset = word_count - i - 1;
word_offset = word_count - i - 1
} else {
word_offset = i;
word_offset = i
}
bits_to_get := min(type_bits, bit_count - bit_offset);
W := internal_int_bitfield_extract(a, bit_offset, bits_to_get) or_return;
buf[word_offset] = T(W);
bits_to_get := min(type_bits, bit_count - bit_offset)
W := internal_int_bitfield_extract(a, bit_offset, bits_to_get) or_return
buf[word_offset] = T(W)
}
return word_count, nil;
return word_count, nil
}
internal_int_unpack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First, allocator := context.allocator) -> (err: Error)
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
assert(nails >= 0 && nails < (size_of(T) * 8));
context.allocator = allocator;
assert(nails >= 0 && nails < (size_of(T) * 8))
context.allocator = allocator
type_size := size_of(T);
type_bits := (type_size * 8) - nails;
type_mask := T(1 << uint(type_bits)) - 1;
type_size := size_of(T)
type_bits := (type_size * 8) - nails
type_mask := T(1 << uint(type_bits)) - 1
if len(buf) == 0 {
return .Invalid_Argument;
return .Invalid_Argument
}
bit_count := type_bits * len(buf);
digit_count := (bit_count / _DIGIT_BITS) + min(1, bit_count % _DIGIT_BITS);
bit_count := type_bits * len(buf)
digit_count := (bit_count / _DIGIT_BITS) + min(1, bit_count % _DIGIT_BITS)
/*
Pre-size output Int.
*/
internal_grow(a, digit_count) or_return;
internal_grow(a, digit_count) or_return
t := &Int{};
defer internal_destroy(t);
t := &Int{}
defer internal_destroy(t)
if order == .LSB_First {
for W, i in buf {
internal_set(t, W & type_mask) or_return;
internal_shl(t, t, type_bits * i) or_return;
internal_add(a, a, t) or_return;
internal_set(t, W & type_mask) or_return
internal_shl(t, t, type_bits * i) or_return
internal_add(a, a, t) or_return
}
} else {
for W in buf {
internal_set(t, W & type_mask) or_return;
internal_shl(a, a, type_bits) or_return;
internal_add(a, a, t) or_return;
internal_set(t, W & type_mask) or_return
internal_shl(a, a, type_bits) or_return
internal_add(a, a, t) or_return
}
}
return internal_clamp(a);
return internal_clamp(a)
}
/*
@@ -557,7 +557,7 @@ internal_int_unpack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_F
for 64 bit "int".
*/
_RADIX_SIZE_SCALE :: 29;
_RADIX_SIZE_SCALE :: 29
_log_bases :: [65]u32{
0, 0, 0x20000001, 0x14309399, 0x10000001,
0xdc81a35, 0xc611924, 0xb660c9e, 0xaaaaaab, 0xa1849cd,
@@ -572,12 +572,12 @@ _log_bases :: [65]u32{
0x5ab7d68, 0x5a42df0, 0x59d1506, 0x5962ffe, 0x58f7c57,
0x588f7bc, 0x582a000, 0x57c7319, 0x5766f1d, 0x5709243,
0x56adad9, 0x565474d, 0x55fd61f, 0x55a85e8, 0x5555556,
};
}
/*
Characters used in radix conversions.
*/
RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"
RADIX_TABLE_REVERSE := [RADIX_TABLE_REVERSE_SIZE]u8{
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */
0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */
@@ -587,59 +587,59 @@ RADIX_TABLE_REVERSE := [RADIX_TABLE_REVERSE_SIZE]u8{
0xff, 0xff, 0xff, 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* ]^_`abcdef */
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */
};
RADIX_TABLE_REVERSE_SIZE :: 80;
}
RADIX_TABLE_REVERSE_SIZE :: 80
/*
Stores a bignum as a ASCII string in a given radix (2..64)
The buffer must be appropriately sized. This routine doesn't check.
*/
_itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false, allocator := context.allocator) -> (written: int, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
assert_if_nil(a)
context.allocator = allocator
temp, denominator := &Int{}, &Int{};
temp, denominator := &Int{}, &Int{}
internal_copy(temp, a) or_return;
internal_set(denominator, radix) or_return;
internal_copy(temp, a) or_return
internal_set(denominator, radix) or_return
available := len(buffer);
available := len(buffer)
if zero_terminate {
available -= 1;
buffer[available] = 0;
available -= 1
buffer[available] = 0
}
if a.sign == .Negative {
temp.sign = .Zero_or_Positive;
temp.sign = .Zero_or_Positive
}
remainder: DIGIT;
remainder: DIGIT
for {
if remainder, err = #force_inline internal_divmod(temp, temp, DIGIT(radix)); err != nil {
internal_destroy(temp, denominator);
return len(buffer) - available, err;
internal_destroy(temp, denominator)
return len(buffer) - available, err
}
available -= 1;
buffer[available] = RADIX_TABLE[remainder];
available -= 1
buffer[available] = RADIX_TABLE[remainder]
if temp.used == 0 {
break;
break
}
}
if a.sign == .Negative {
available -= 1;
buffer[available] = '-';
available -= 1
buffer[available] = '-'
}
internal_destroy(temp, denominator);
internal_destroy(temp, denominator)
/*
If we overestimated the size, we need to move the buffer left.
*/
written = len(buffer) - available;
written = len(buffer) - available
if written < len(buffer) {
diff := len(buffer) - written;
mem.copy(&buffer[0], &buffer[diff], written);
diff := len(buffer) - written
mem.copy(&buffer[0], &buffer[diff], written)
}
return written, nil;
return written, nil
}
+198 -198
View File
@@ -24,111 +24,111 @@ PyRes :: struct {
}
@export test_initialize_constants :: proc "c" () -> (res: u64) {
context = runtime.default_context();
res = u64(initialize_constants());
context = runtime.default_context()
res = u64(initialize_constants())
//assert(MUL_KARATSUBA_CUTOFF >= 40);
return res;
return res
}
@export test_error_string :: proc "c" (err: Error) -> (res: cstring) {
context = runtime.default_context();
es := Error_String;
return strings.clone_to_cstring(es[err], context.temp_allocator);
context = runtime.default_context()
es := Error_String
return strings.clone_to_cstring(es[err], context.temp_allocator)
}
@export test_add :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
aa, bb, sum := &Int{}, &Int{}, &Int{};
defer internal_destroy(aa, bb, sum);
aa, bb, sum := &Int{}, &Int{}, &Int{}
defer internal_destroy(aa, bb, sum)
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":add:atoi(a):", err=err}; }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":add:atoi(b):", err=err}; }
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":add:atoi(a):", err=err} }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":add:atoi(b):", err=err} }
if bb.used == 1 {
if err = #force_inline internal_add(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; }
if err = #force_inline internal_add(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err} }
} else {
if err = #force_inline internal_add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; }
if err = #force_inline internal_add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err} }
}
r: cstring;
r, err = int_itoa_cstring(sum, 16, context.temp_allocator);
if err != nil { return PyRes{res=":add:itoa(sum):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(sum, 16, context.temp_allocator)
if err != nil { return PyRes{res=":add:itoa(sum):", err=err} }
return PyRes{res = r, err = nil}
}
@export test_sub :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
aa, bb, sum := &Int{}, &Int{}, &Int{};
defer internal_destroy(aa, bb, sum);
aa, bb, sum := &Int{}, &Int{}, &Int{}
defer internal_destroy(aa, bb, sum)
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sub:atoi(a):", err=err}; }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":sub:atoi(b):", err=err}; }
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sub:atoi(a):", err=err} }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":sub:atoi(b):", err=err} }
if bb.used == 1 {
if err = #force_inline internal_sub(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; }
if err = #force_inline internal_sub(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err} }
} else {
if err = #force_inline internal_sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; }
if err = #force_inline internal_sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err} }
}
r: cstring;
r, err = int_itoa_cstring(sum, 16, context.temp_allocator);
if err != nil { return PyRes{res=":sub:itoa(sum):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(sum, 16, context.temp_allocator)
if err != nil { return PyRes{res=":sub:itoa(sum):", err=err} }
return PyRes{res = r, err = nil}
}
@export test_mul :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
aa, bb, product := &Int{}, &Int{}, &Int{};
defer internal_destroy(aa, bb, product);
aa, bb, product := &Int{}, &Int{}, &Int{}
defer internal_destroy(aa, bb, product)
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":mul:atoi(a):", err=err}; }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":mul:atoi(b):", err=err}; }
if err = #force_inline internal_mul(product, aa, bb); err != nil { return PyRes{res=":mul:mul(product,a,b):", err=err}; }
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":mul:atoi(a):", err=err} }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":mul:atoi(b):", err=err} }
if err = #force_inline internal_mul(product, aa, bb); err != nil { return PyRes{res=":mul:mul(product,a,b):", err=err} }
r: cstring;
r, err = int_itoa_cstring(product, 16, context.temp_allocator);
if err != nil { return PyRes{res=":mul:itoa(product):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(product, 16, context.temp_allocator)
if err != nil { return PyRes{res=":mul:itoa(product):", err=err} }
return PyRes{res = r, err = nil}
}
@export test_sqr :: proc "c" (a: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
aa, square := &Int{}, &Int{};
defer internal_destroy(aa, square);
aa, square := &Int{}, &Int{}
defer internal_destroy(aa, square)
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sqr:atoi(a):", err=err}; }
if err = #force_inline internal_sqr(square, aa); err != nil { return PyRes{res=":sqr:sqr(square,a):", err=err}; }
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sqr:atoi(a):", err=err} }
if err = #force_inline internal_sqr(square, aa); err != nil { return PyRes{res=":sqr:sqr(square,a):", err=err} }
r: cstring;
r, err = int_itoa_cstring(square, 16, context.temp_allocator);
if err != nil { return PyRes{res=":sqr:itoa(square):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(square, 16, context.temp_allocator)
if err != nil { return PyRes{res=":sqr:itoa(square):", err=err} }
return PyRes{res = r, err = nil}
}
/*
NOTE(Jeroen): For simplicity, we don't return the quotient and the remainder, just the quotient.
*/
@export test_div :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
aa, bb, quotient := &Int{}, &Int{}, &Int{};
defer internal_destroy(aa, bb, quotient);
aa, bb, quotient := &Int{}, &Int{}, &Int{}
defer internal_destroy(aa, bb, quotient)
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":div:atoi(a):", err=err}; }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":div:atoi(b):", err=err}; }
if err = #force_inline internal_div(quotient, aa, bb); err != nil { return PyRes{res=":div:div(quotient,a,b):", err=err}; }
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":div:atoi(a):", err=err} }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":div:atoi(b):", err=err} }
if err = #force_inline internal_div(quotient, aa, bb); err != nil { return PyRes{res=":div:div(quotient,a,b):", err=err} }
r: cstring;
r, err = int_itoa_cstring(quotient, 16, context.temp_allocator);
if err != nil { return PyRes{res=":div:itoa(quotient):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(quotient, 16, context.temp_allocator)
if err != nil { return PyRes{res=":div:itoa(quotient):", err=err} }
return PyRes{res = r, err = nil}
}
@@ -136,254 +136,254 @@ PyRes :: struct {
res = log(a, base)
*/
@export test_log :: proc "c" (a: cstring, base := DIGIT(2)) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
l: int;
context = runtime.default_context()
err: Error
l: int
aa := &Int{};
defer internal_destroy(aa);
aa := &Int{}
defer internal_destroy(aa)
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":log:atoi(a):", err=err}; }
if l, err = #force_inline internal_log(aa, base); err != nil { return PyRes{res=":log:log(a, base):", err=err}; }
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":log:atoi(a):", err=err} }
if l, err = #force_inline internal_log(aa, base); err != nil { return PyRes{res=":log:log(a, base):", err=err} }
#force_inline internal_zero(aa);
aa.digit[0] = DIGIT(l) & _MASK;
aa.digit[1] = DIGIT(l) >> _DIGIT_BITS;
aa.used = 2;
clamp(aa);
#force_inline internal_zero(aa)
aa.digit[0] = DIGIT(l) & _MASK
aa.digit[1] = DIGIT(l) >> _DIGIT_BITS
aa.used = 2
clamp(aa)
r: cstring;
r, err = int_itoa_cstring(aa, 16, context.temp_allocator);
if err != nil { return PyRes{res=":log:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(aa, 16, context.temp_allocator)
if err != nil { return PyRes{res=":log:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = base^power
*/
@export test_pow :: proc "c" (base: cstring, power := int(2)) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
dest, bb := &Int{}, &Int{};
defer internal_destroy(dest, bb);
dest, bb := &Int{}, &Int{}
defer internal_destroy(dest, bb)
if err = atoi(bb, string(base), 16); err != nil { return PyRes{res=":pow:atoi(base):", err=err}; }
if err = #force_inline internal_pow(dest, bb, power); err != nil { return PyRes{res=":pow:pow(dest, base, power):", err=err}; }
if err = atoi(bb, string(base), 16); err != nil { return PyRes{res=":pow:atoi(base):", err=err} }
if err = #force_inline internal_pow(dest, bb, power); err != nil { return PyRes{res=":pow:pow(dest, base, power):", err=err} }
r: cstring;
r, err = int_itoa_cstring(dest, 16, context.temp_allocator);
if err != nil { return PyRes{res=":log:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(dest, 16, context.temp_allocator)
if err != nil { return PyRes{res=":log:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = sqrt(src)
*/
@export test_sqrt :: proc "c" (source: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
src := &Int{};
defer internal_destroy(src);
src := &Int{}
defer internal_destroy(src)
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":sqrt:atoi(src):", err=err}; }
if err = #force_inline internal_sqrt(src, src); err != nil { return PyRes{res=":sqrt:sqrt(src):", err=err}; }
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":sqrt:atoi(src):", err=err} }
if err = #force_inline internal_sqrt(src, src); err != nil { return PyRes{res=":sqrt:sqrt(src):", err=err} }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":log:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(src, 16, context.temp_allocator)
if err != nil { return PyRes{res=":log:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = root_n(src, power)
*/
@export test_root_n :: proc "c" (source: cstring, power: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
src := &Int{};
defer internal_destroy(src);
src := &Int{}
defer internal_destroy(src)
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":root_n:atoi(src):", err=err}; }
if err = #force_inline internal_root_n(src, src, power); err != nil { return PyRes{res=":root_n:root_n(src):", err=err}; }
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":root_n:atoi(src):", err=err} }
if err = #force_inline internal_root_n(src, src, power); err != nil { return PyRes{res=":root_n:root_n(src):", err=err} }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":root_n:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(src, 16, context.temp_allocator)
if err != nil { return PyRes{res=":root_n:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = shr_digit(src, digits)
*/
@export test_shr_digit :: proc "c" (source: cstring, digits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
src := &Int{};
defer internal_destroy(src);
src := &Int{}
defer internal_destroy(src)
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_digit:atoi(src):", err=err}; }
if err = #force_inline internal_shr_digit(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err}; }
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_digit:atoi(src):", err=err} }
if err = #force_inline internal_shr_digit(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err} }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shr_digit:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(src, 16, context.temp_allocator)
if err != nil { return PyRes{res=":shr_digit:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = shl_digit(src, digits)
*/
@export test_shl_digit :: proc "c" (source: cstring, digits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
src := &Int{};
defer internal_destroy(src);
src := &Int{}
defer internal_destroy(src)
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl_digit:atoi(src):", err=err}; }
if err = #force_inline internal_shl_digit(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err}; }
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl_digit:atoi(src):", err=err} }
if err = #force_inline internal_shl_digit(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err} }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shl_digit:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(src, 16, context.temp_allocator)
if err != nil { return PyRes{res=":shl_digit:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = shr(src, bits)
*/
@export test_shr :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
src := &Int{};
defer internal_destroy(src);
src := &Int{}
defer internal_destroy(src)
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr:atoi(src):", err=err}; }
if err = #force_inline internal_shr(src, src, bits); err != nil { return PyRes{res=":shr:shr(src, bits):", err=err}; }
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr:atoi(src):", err=err} }
if err = #force_inline internal_shr(src, src, bits); err != nil { return PyRes{res=":shr:shr(src, bits):", err=err} }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shr:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(src, 16, context.temp_allocator)
if err != nil { return PyRes{res=":shr:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = shr_signed(src, bits)
*/
@export test_shr_signed :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
src := &Int{};
defer internal_destroy(src);
src := &Int{}
defer internal_destroy(src)
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_signed:atoi(src):", err=err}; }
if err = #force_inline internal_shr_signed(src, src, bits); err != nil { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err}; }
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_signed:atoi(src):", err=err} }
if err = #force_inline internal_shr_signed(src, src, bits); err != nil { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err} }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shr_signed:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(src, 16, context.temp_allocator)
if err != nil { return PyRes{res=":shr_signed:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = shl(src, bits)
*/
@export test_shl :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
src := &Int{};
defer internal_destroy(src);
src := &Int{}
defer internal_destroy(src)
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl:atoi(src):", err=err}; }
if err = #force_inline internal_shl(src, src, bits); err != nil { return PyRes{res=":shl:shl(src, bits):", err=err}; }
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl:atoi(src):", err=err} }
if err = #force_inline internal_shl(src, src, bits); err != nil { return PyRes{res=":shl:shl(src, bits):", err=err} }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shl:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(src, 16, context.temp_allocator)
if err != nil { return PyRes{res=":shl:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = factorial(n)
*/
@export test_factorial :: proc "c" (n: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
dest := &Int{};
defer internal_destroy(dest);
dest := &Int{}
defer internal_destroy(dest)
if err = #force_inline internal_int_factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err}; }
if err = #force_inline internal_int_factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err} }
r: cstring;
r, err = int_itoa_cstring(dest, 16, context.temp_allocator);
if err != nil { return PyRes{res=":factorial:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(dest, 16, context.temp_allocator)
if err != nil { return PyRes{res=":factorial:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = gcd(a, b)
*/
@export test_gcd :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
ai, bi, dest := &Int{}, &Int{}, &Int{};
defer internal_destroy(ai, bi, dest);
ai, bi, dest := &Int{}, &Int{}, &Int{}
defer internal_destroy(ai, bi, dest)
if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":gcd:atoi(a):", err=err}; }
if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":gcd:atoi(b):", err=err}; }
if err = #force_inline internal_int_gcd_lcm(dest, nil, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err}; }
if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":gcd:atoi(a):", err=err} }
if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":gcd:atoi(b):", err=err} }
if err = #force_inline internal_int_gcd_lcm(dest, nil, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err} }
r: cstring;
r, err = int_itoa_cstring(dest, 16, context.temp_allocator);
if err != nil { return PyRes{res=":gcd:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(dest, 16, context.temp_allocator)
if err != nil { return PyRes{res=":gcd:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = lcm(a, b)
*/
@export test_lcm :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
context = runtime.default_context()
err: Error
ai, bi, dest := &Int{}, &Int{}, &Int{};
defer internal_destroy(ai, bi, dest);
ai, bi, dest := &Int{}, &Int{}, &Int{}
defer internal_destroy(ai, bi, dest)
if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":lcm:atoi(a):", err=err}; }
if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":lcm:atoi(b):", err=err}; }
if err = #force_inline internal_int_gcd_lcm(nil, dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err}; }
if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":lcm:atoi(a):", err=err} }
if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":lcm:atoi(b):", err=err} }
if err = #force_inline internal_int_gcd_lcm(nil, dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err} }
r: cstring;
r, err = int_itoa_cstring(dest, 16, context.temp_allocator);
if err != nil { return PyRes{res=":lcm:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
r: cstring
r, err = int_itoa_cstring(dest, 16, context.temp_allocator)
if err != nil { return PyRes{res=":lcm:itoa(res):", err=err} }
return PyRes{res = r, err = nil}
}
/*
dest = lcm(a, b)
*/
@export test_is_square :: proc "c" (a: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
square: bool;
context = runtime.default_context()
err: Error
square: bool
ai := &Int{};
defer internal_destroy(ai);
ai := &Int{}
defer internal_destroy(ai)
if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":is_square:atoi(a):", err=err}; }
if square, err = #force_inline internal_int_is_square(ai); err != nil { return PyRes{res=":is_square:is_square(a):", err=err}; }
if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":is_square:atoi(a):", err=err} }
if square, err = #force_inline internal_int_is_square(ai); err != nil { return PyRes{res=":is_square:is_square(a):", err=err} }
if square {
return PyRes{"True", nil};
return PyRes{"True", nil}
}
return PyRes{"False", nil};
return PyRes{"False", nil}
}
+19 -19
View File
@@ -25,58 +25,58 @@ Category :: enum {
rm_trials,
is_prime,
random_prime,
};
}
Event :: struct {
ticks: time.Duration,
count: int,
cycles: u64,
}
Timings := [Category]Event{};
Timings := [Category]Event{}
print_timings :: proc() {
duration :: proc(d: time.Duration) -> (res: string) {
switch {
case d < time.Microsecond:
return fmt.tprintf("%v ns", time.duration_nanoseconds(d));
return fmt.tprintf("%v ns", time.duration_nanoseconds(d))
case d < time.Millisecond:
return fmt.tprintf("%v µs", time.duration_microseconds(d));
return fmt.tprintf("%v µs", time.duration_microseconds(d))
case:
return fmt.tprintf("%v ms", time.duration_milliseconds(d));
return fmt.tprintf("%v ms", time.duration_milliseconds(d))
}
}
for v in Timings {
if v.count > 0 {
fmt.println("\nTimings:");
break;
fmt.println("\nTimings:")
break
}
}
for v, i in Timings {
if v.count > 0 {
avg_ticks := time.Duration(f64(v.ticks) / f64(v.count));
avg_cycles := f64(v.cycles) / f64(v.count);
avg_ticks := time.Duration(f64(v.ticks) / f64(v.count))
avg_cycles := f64(v.cycles) / f64(v.count)
fmt.printf("\t%v: %s / %v cycles (avg), %s / %v cycles (total, %v calls)\n", i, duration(avg_ticks), avg_cycles, duration(v.ticks), v.cycles, v.count);
fmt.printf("\t%v: %s / %v cycles (avg), %s / %v cycles (total, %v calls)\n", i, duration(avg_ticks), avg_cycles, duration(v.ticks), v.cycles, v.count)
}
}
}
@(deferred_in_out=_SCOPE_END)
SCOPED_TIMING :: #force_inline proc(c: Category) -> (ticks: time.Tick, cycles: u64) {
cycles = time.read_cycle_counter();
ticks = time.tick_now();
return;
cycles = time.read_cycle_counter()
ticks = time.tick_now()
return
}
_SCOPE_END :: #force_inline proc(c: Category, ticks: time.Tick, cycles: u64) {
cycles_now := time.read_cycle_counter();
ticks_now := time.tick_now();
cycles_now := time.read_cycle_counter()
ticks_now := time.tick_now()
Timings[c].ticks = time.tick_diff(ticks, ticks_now);
Timings[c].cycles = cycles_now - cycles;
Timings[c].count += 1;
Timings[c].ticks = time.tick_diff(ticks, ticks_now)
Timings[c].cycles = cycles_now - cycles
Timings[c].count += 1
}
SCOPED_COUNT_ADD :: #force_inline proc(c: Category, count: int) {
Timings[c].count += count;
Timings[c].count += count
}
+230 -230
View File
@@ -2,308 +2,308 @@ package math_bits
import "core:intrinsics"
U8_MIN :: 0;
U16_MIN :: 0;
U32_MIN :: 0;
U64_MIN :: 0;
U8_MIN :: 0
U16_MIN :: 0
U32_MIN :: 0
U64_MIN :: 0
U8_MAX :: 1 << 8 - 1;
U16_MAX :: 1 << 16 - 1;
U32_MAX :: 1 << 32 - 1;
U64_MAX :: 1 << 64 - 1;
U8_MAX :: 1 << 8 - 1
U16_MAX :: 1 << 16 - 1
U32_MAX :: 1 << 32 - 1
U64_MAX :: 1 << 64 - 1
I8_MIN :: - 1 << 7;
I16_MIN :: - 1 << 15;
I32_MIN :: - 1 << 31;
I64_MIN :: - 1 << 63;
I8_MIN :: - 1 << 7
I16_MIN :: - 1 << 15
I32_MIN :: - 1 << 31
I64_MIN :: - 1 << 63
I8_MAX :: 1 << 7 - 1;
I16_MAX :: 1 << 15 - 1;
I32_MAX :: 1 << 31 - 1;
I64_MAX :: 1 << 63 - 1;
I8_MAX :: 1 << 7 - 1
I16_MAX :: 1 << 15 - 1
I32_MAX :: 1 << 31 - 1
I64_MAX :: 1 << 63 - 1
count_ones :: intrinsics.count_ones;
count_zeros :: intrinsics.count_zeros;
trailing_zeros :: intrinsics.count_trailing_zeros;
leading_zeros :: intrinsics.count_leading_zeros;
count_trailing_zeros :: intrinsics.count_trailing_zeros;
count_leading_zeros :: intrinsics.count_leading_zeros;
reverse_bits :: intrinsics.reverse_bits;
byte_swap :: intrinsics.byte_swap;
count_ones :: intrinsics.count_ones
count_zeros :: intrinsics.count_zeros
trailing_zeros :: intrinsics.count_trailing_zeros
leading_zeros :: intrinsics.count_leading_zeros
count_trailing_zeros :: intrinsics.count_trailing_zeros
count_leading_zeros :: intrinsics.count_leading_zeros
reverse_bits :: intrinsics.reverse_bits
byte_swap :: intrinsics.byte_swap
overflowing_add :: intrinsics.overflow_add;
overflowing_sub :: intrinsics.overflow_sub;
overflowing_mul :: intrinsics.overflow_mul;
overflowing_add :: intrinsics.overflow_add
overflowing_sub :: intrinsics.overflow_sub
overflowing_mul :: intrinsics.overflow_mul
rotate_left8 :: proc(x: u8, k: int) -> u8 {
n :: 8;
s := uint(k) & (n-1);
return x <<s | x>>(n-s);
n :: 8
s := uint(k) & (n-1)
return x <<s | x>>(n-s)
}
rotate_left16 :: proc(x: u16, k: int) -> u16 {
n :: 16;
s := uint(k) & (n-1);
return x <<s | x>>(n-s);
n :: 16
s := uint(k) & (n-1)
return x <<s | x>>(n-s)
}
rotate_left32 :: proc(x: u32, k: int) -> u32 {
n :: 32;
s := uint(k) & (n-1);
return x <<s | x>>(n-s);
n :: 32
s := uint(k) & (n-1)
return x <<s | x>>(n-s)
}
rotate_left64 :: proc(x: u64, k: int) -> u64 {
n :: 64;
s := uint(k) & (n-1);
return x <<s | x>>(n-s);
n :: 64
s := uint(k) & (n-1)
return x <<s | x>>(n-s)
}
rotate_left :: proc(x: uint, k: int) -> uint {
n :: 8*size_of(uint);
s := uint(k) & (n-1);
return x <<s | x>>(n-s);
n :: 8*size_of(uint)
s := uint(k) & (n-1)
return x <<s | x>>(n-s)
}
from_be_u8 :: proc(i: u8) -> u8 { return i; }
from_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_u8 :: proc(i: u8) -> u8 { return i }
from_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
from_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
from_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
from_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
from_le_u8 :: proc(i: u8) -> u8 { return i; }
from_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_u8 :: proc(i: u8) -> u8 { return i }
from_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
from_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
from_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
from_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
to_be_u8 :: proc(i: u8) -> u8 { return i; }
to_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_u8 :: proc(i: u8) -> u8 { return i }
to_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
to_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
to_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
to_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
to_le_u8 :: proc(i: u8) -> u8 { return i; }
to_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le_u8 :: proc(i: u8) -> u8 { return i }
to_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
to_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
len_u8 :: proc(x: u8) -> int {
return int(len_u8_table[x]);
return int(len_u8_table[x])
}
len_u16 :: proc(x: u16) -> (n: int) {
x := x;
x := x
if x >= 1<<8 {
x >>= 8;
n = 8;
x >>= 8
n = 8
}
return n + int(len_u8_table[x]);
return n + int(len_u8_table[x])
}
len_u32 :: proc(x: u32) -> (n: int) {
x := x;
x := x
if x >= 1<<16 {
x >>= 16;
n = 16;
x >>= 16
n = 16
}
if x >= 1<<8 {
x >>= 8;
n += 8;
x >>= 8
n += 8
}
return n + int(len_u8_table[x]);
return n + int(len_u8_table[x])
}
len_u64 :: proc(x: u64) -> (n: int) {
x := x;
x := x
if x >= 1<<32 {
x >>= 32;
n = 32;
x >>= 32
n = 32
}
if x >= 1<<16 {
x >>= 16;
n += 16;
x >>= 16
n += 16
}
if x >= 1<<8 {
x >>= 8;
n += 8;
x >>= 8
n += 8
}
return n + int(len_u8_table[x]);
return n + int(len_u8_table[x])
}
len_uint :: proc(x: uint) -> (n: int) {
when size_of(uint) == size_of(u64) {
return len_u64(u64(x));
return len_u64(u64(x))
} else {
return len_u32(u32(x));
return len_u32(u32(x))
}
}
// returns the minimum number of bits required to represent x
len :: proc{len_u8, len_u16, len_u32, len_u64, len_uint};
len :: proc{len_u8, len_u16, len_u32, len_u64, len_uint}
add_u32 :: proc(x, y, carry: u32) -> (sum, carry_out: u32) {
yc := y + carry;
sum = x + yc;
yc := y + carry
sum = x + yc
if sum < x || yc < y {
carry_out = 1;
carry_out = 1
}
return;
return
}
add_u64 :: proc(x, y, carry: u64) -> (sum, carry_out: u64) {
yc := y + carry;
sum = x + yc;
yc := y + carry
sum = x + yc
if sum < x || yc < y {
carry_out = 1;
carry_out = 1
}
return;
return
}
add_uint :: proc(x, y, carry: uint) -> (sum, carry_out: uint) {
yc := y + carry;
sum = x + yc;
yc := y + carry
sum = x + yc
if sum < x || yc < y {
carry_out = 1;
carry_out = 1
}
return;
return
}
add :: proc{add_u32, add_u64, add_uint};
add :: proc{add_u32, add_u64, add_uint}
sub_u32 :: proc(x, y, borrow: u32) -> (diff, borrow_out: u32) {
yb := y + borrow;
diff = x - yb;
yb := y + borrow
diff = x - yb
if diff > x || yb < y {
borrow_out = 1;
borrow_out = 1
}
return;
return
}
sub_u64 :: proc(x, y, borrow: u64) -> (diff, borrow_out: u64) {
yb := y + borrow;
diff = x - yb;
yb := y + borrow
diff = x - yb
if diff > x || yb < y {
borrow_out = 1;
borrow_out = 1
}
return;
return
}
sub_uint :: proc(x, y, borrow: uint) -> (diff, borrow_out: uint) {
yb := y + borrow;
diff = x - yb;
yb := y + borrow
diff = x - yb
if diff > x || yb < y {
borrow_out = 1;
borrow_out = 1
}
return;
return
}
sub :: proc{sub_u32, sub_u64, sub_uint};
sub :: proc{sub_u32, sub_u64, sub_uint}
mul_u32 :: proc(x, y: u32) -> (hi, lo: u32) {
z := u64(x) * u64(y);
hi, lo = u32(z>>32), u32(z);
return;
z := u64(x) * u64(y)
hi, lo = u32(z>>32), u32(z)
return
}
mul_u64 :: proc(x, y: u64) -> (hi, lo: u64) {
mask :: 1<<32 - 1;
mask :: 1<<32 - 1
x0, x1 := x & mask, x >> 32;
y0, y1 := y & mask, y >> 32;
x0, x1 := x & mask, x >> 32
y0, y1 := y & mask, y >> 32
w0 := x0 * y0;
t := x1*y0 + w0>>32;
w0 := x0 * y0
t := x1*y0 + w0>>32
w1, w2 := t & mask, t >> 32;
w1 += x0 * y1;
hi = x1*y1 + w2 + w1>>32;
lo = x * y;
return;
w1, w2 := t & mask, t >> 32
w1 += x0 * y1
hi = x1*y1 + w2 + w1>>32
lo = x * y
return
}
mul_uint :: proc(x, y: uint) -> (hi, lo: uint) {
when size_of(uint) == size_of(u32) {
a, b := mul_u32(u32(x), u32(y));
a, b := mul_u32(u32(x), u32(y))
} else {
#assert(size_of(uint) == size_of(u64));
a, b := mul_u64(u64(x), u64(y));
#assert(size_of(uint) == size_of(u64))
a, b := mul_u64(u64(x), u64(y))
}
return uint(a), uint(b);
return uint(a), uint(b)
}
mul :: proc{mul_u32, mul_u64, mul_uint};
mul :: proc{mul_u32, mul_u64, mul_uint}
div_u32 :: proc(hi, lo, y: u32) -> (quo, rem: u32) {
assert(y != 0 && y <= hi);
z := u64(hi)<<32 | u64(lo);
quo, rem = u32(z/u64(y)), u32(z%u64(y));
return;
assert(y != 0 && y <= hi)
z := u64(hi)<<32 | u64(lo)
quo, rem = u32(z/u64(y)), u32(z%u64(y))
return
}
div_u64 :: proc(hi, lo, y: u64) -> (quo, rem: u64) {
y := y;
two32 :: 1 << 32;
mask32 :: two32 - 1;
y := y
two32 :: 1 << 32
mask32 :: two32 - 1
if y == 0 {
panic("divide error");
panic("divide error")
}
if y <= hi {
panic("overflow error");
panic("overflow error")
}
s := uint(count_leading_zeros(y));
y <<= s;
s := uint(count_leading_zeros(y))
y <<= s
yn1 := y >> 32;
yn0 := y & mask32;
un32 := hi<<s | lo>>(64-s);
un10 := lo << s;
un1 := un10 >> 32;
un0 := un10 & mask32;
q1 := un32 / yn1;
rhat := un32 - q1*yn1;
yn1 := y >> 32
yn0 := y & mask32
un32 := hi<<s | lo>>(64-s)
un10 := lo << s
un1 := un10 >> 32
un0 := un10 & mask32
q1 := un32 / yn1
rhat := un32 - q1*yn1
for q1 >= two32 || q1*yn0 > two32*rhat+un1 {
q1 -= 1;
rhat += yn1;
q1 -= 1
rhat += yn1
if rhat >= two32 {
break;
break
}
}
un21 := un32*two32 + un1 - q1*y;
q0 := un21 / yn1;
rhat = un21 - q0*yn1;
un21 := un32*two32 + un1 - q1*y
q0 := un21 / yn1
rhat = un21 - q0*yn1
for q0 >= two32 || q0*yn0 > two32*rhat+un0 {
q0 -= 1;
rhat += yn1;
q0 -= 1
rhat += yn1
if rhat >= two32 {
break;
break
}
}
return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s;
return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s
}
div_uint :: proc(hi, lo, y: uint) -> (quo, rem: uint) {
when size_of(uint) == size_of(u32) {
a, b := div_u32(u32(hi), u32(lo), u32(y));
a, b := div_u32(u32(hi), u32(lo), u32(y))
} else {
#assert(size_of(uint) == size_of(u64));
a, b := div_u64(u64(hi), u64(lo), u64(y));
#assert(size_of(uint) == size_of(u64))
a, b := div_u64(u64(hi), u64(lo), u64(y))
}
return uint(a), uint(b);
return uint(a), uint(b)
}
div :: proc{div_u32, div_u64, div_uint};
div :: proc{div_u32, div_u64, div_uint}
is_power_of_two_u8 :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_i8 :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_u16 :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_i16 :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_u32 :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_i32 :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_u64 :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_i64 :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_uint :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_int :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_u8 :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two_i8 :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two_u16 :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two_i16 :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two_u32 :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two_i32 :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two_u64 :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two_i64 :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two_uint :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two_int :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two :: proc{
is_power_of_two_u8, is_power_of_two_i8,
@@ -311,7 +311,7 @@ is_power_of_two :: proc{
is_power_of_two_u32, is_power_of_two_i32,
is_power_of_two_u64, is_power_of_two_i64,
is_power_of_two_uint, is_power_of_two_int,
};
}
@private
@@ -325,51 +325,51 @@ len_u8_table := [256]u8{
32..<64 = 6,
64..<128 = 7,
128..<256 = 8,
};
}
bitfield_extract_u8 :: proc(value: u8, offset, bits: uint) -> u8 { return (value >> offset) & u8(1<<bits - 1); }
bitfield_extract_u16 :: proc(value: u16, offset, bits: uint) -> u16 { return (value >> offset) & u16(1<<bits - 1); }
bitfield_extract_u32 :: proc(value: u32, offset, bits: uint) -> u32 { return (value >> offset) & u32(1<<bits - 1); }
bitfield_extract_u64 :: proc(value: u64, offset, bits: uint) -> u64 { return (value >> offset) & u64(1<<bits - 1); }
bitfield_extract_u128 :: proc(value: u128, offset, bits: uint) -> u128 { return (value >> offset) & u128(1<<bits - 1); }
bitfield_extract_uint :: proc(value: uint, offset, bits: uint) -> uint { return (value >> offset) & uint(1<<bits - 1); }
bitfield_extract_u8 :: proc(value: u8, offset, bits: uint) -> u8 { return (value >> offset) & u8(1<<bits - 1) }
bitfield_extract_u16 :: proc(value: u16, offset, bits: uint) -> u16 { return (value >> offset) & u16(1<<bits - 1) }
bitfield_extract_u32 :: proc(value: u32, offset, bits: uint) -> u32 { return (value >> offset) & u32(1<<bits - 1) }
bitfield_extract_u64 :: proc(value: u64, offset, bits: uint) -> u64 { return (value >> offset) & u64(1<<bits - 1) }
bitfield_extract_u128 :: proc(value: u128, offset, bits: uint) -> u128 { return (value >> offset) & u128(1<<bits - 1) }
bitfield_extract_uint :: proc(value: uint, offset, bits: uint) -> uint { return (value >> offset) & uint(1<<bits - 1) }
bitfield_extract_i8 :: proc(value: i8, offset, bits: uint) -> i8 {
v := (u8(value) >> offset) & u8(1<<bits - 1);
m := u8(1<<(bits-1));
r := (v~m) - m;
return i8(r);
v := (u8(value) >> offset) & u8(1<<bits - 1)
m := u8(1<<(bits-1))
r := (v~m) - m
return i8(r)
}
bitfield_extract_i16 :: proc(value: i16, offset, bits: uint) -> i16 {
v := (u16(value) >> offset) & u16(1<<bits - 1);
m := u16(1<<(bits-1));
r := (v~m) - m;
return i16(r);
v := (u16(value) >> offset) & u16(1<<bits - 1)
m := u16(1<<(bits-1))
r := (v~m) - m
return i16(r)
}
bitfield_extract_i32 :: proc(value: i32, offset, bits: uint) -> i32 {
v := (u32(value) >> offset) & u32(1<<bits - 1);
m := u32(1<<(bits-1));
r := (v~m) - m;
return i32(r);
v := (u32(value) >> offset) & u32(1<<bits - 1)
m := u32(1<<(bits-1))
r := (v~m) - m
return i32(r)
}
bitfield_extract_i64 :: proc(value: i64, offset, bits: uint) -> i64 {
v := (u64(value) >> offset) & u64(1<<bits - 1);
m := u64(1<<(bits-1));
r := (v~m) - m;
return i64(r);
v := (u64(value) >> offset) & u64(1<<bits - 1)
m := u64(1<<(bits-1))
r := (v~m) - m
return i64(r)
}
bitfield_extract_i128 :: proc(value: i128, offset, bits: uint) -> i128 {
v := (u128(value) >> offset) & u128(1<<bits - 1);
m := u128(1<<(bits-1));
r := (v~m) - m;
return i128(r);
v := (u128(value) >> offset) & u128(1<<bits - 1)
m := u128(1<<(bits-1))
r := (v~m) - m
return i128(r)
}
bitfield_extract_int :: proc(value: int, offset, bits: uint) -> int {
v := (uint(value) >> offset) & uint(1<<bits - 1);
m := uint(1<<(bits-1));
r := (v~m) - m;
return int(r);
v := (uint(value) >> offset) & uint(1<<bits - 1)
m := uint(1<<(bits-1))
r := (v~m) - m
return int(r)
}
@@ -386,57 +386,57 @@ bitfield_extract :: proc{
bitfield_extract_i64,
bitfield_extract_i128,
bitfield_extract_int,
};
}
bitfield_insert_u8 :: proc(base, insert: u8, offset, bits: uint) -> u8 {
mask := u8(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := u8(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_u16 :: proc(base, insert: u16, offset, bits: uint) -> u16 {
mask := u16(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := u16(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_u32 :: proc(base, insert: u32, offset, bits: uint) -> u32 {
mask := u32(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := u32(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_u64 :: proc(base, insert: u64, offset, bits: uint) -> u64 {
mask := u64(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := u64(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_u128 :: proc(base, insert: u128, offset, bits: uint) -> u128 {
mask := u128(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := u128(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_uint :: proc(base, insert: uint, offset, bits: uint) -> uint {
mask := uint(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := uint(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_i8 :: proc(base, insert: i8, offset, bits: uint) -> i8 {
mask := i8(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := i8(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_i16 :: proc(base, insert: i16, offset, bits: uint) -> i16 {
mask := i16(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := i16(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_i32 :: proc(base, insert: i32, offset, bits: uint) -> i32 {
mask := i32(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := i32(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_i64 :: proc(base, insert: i64, offset, bits: uint) -> i64 {
mask := i64(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := i64(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_i128 :: proc(base, insert: i128, offset, bits: uint) -> i128 {
mask := i128(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := i128(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert_int :: proc(base, insert: int, offset, bits: uint) -> int {
mask := int(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
mask := int(1<<bits - 1)
return (base &~ (mask<<offset)) | ((insert&mask) << offset)
}
bitfield_insert :: proc{
@@ -452,4 +452,4 @@ bitfield_insert :: proc{
bitfield_insert_i64,
bitfield_insert_i128,
bitfield_insert_int,
};
}
+60 -60
View File
@@ -4,7 +4,7 @@ import "core:math"
import "core:strconv"
import "core:intrinsics"
_ :: intrinsics;
_ :: intrinsics
Fixed :: struct($Backing: typeid, $Fraction_Width: uint)
where
@@ -14,120 +14,120 @@ Fixed :: struct($Backing: typeid, $Fraction_Width: uint)
i: Backing,
}
Fixed4_4 :: distinct Fixed(i8, 4);
Fixed5_3 :: distinct Fixed(i8, 3);
Fixed6_2 :: distinct Fixed(i8, 2);
Fixed7_1 :: distinct Fixed(i8, 1);
Fixed4_4 :: distinct Fixed(i8, 4)
Fixed5_3 :: distinct Fixed(i8, 3)
Fixed6_2 :: distinct Fixed(i8, 2)
Fixed7_1 :: distinct Fixed(i8, 1)
Fixed8_8 :: distinct Fixed(i16, 8);
Fixed13_3 :: distinct Fixed(i16, 3);
Fixed8_8 :: distinct Fixed(i16, 8)
Fixed13_3 :: distinct Fixed(i16, 3)
Fixed16_16 :: distinct Fixed(i32, 16);
Fixed26_6 :: distinct Fixed(i32, 6);
Fixed16_16 :: distinct Fixed(i32, 16)
Fixed26_6 :: distinct Fixed(i32, 6)
Fixed32_32 :: distinct Fixed(i64, 32);
Fixed52_12 :: distinct Fixed(i64, 12);
Fixed32_32 :: distinct Fixed(i64, 32)
Fixed52_12 :: distinct Fixed(i64, 12)
init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
i, f := math.modf(val);
x.i = Backing(f * (1<<Fraction_Width));
x.i &= 1<<Fraction_Width - 1;
x.i |= Backing(i) << Fraction_Width;
i, f := math.modf(val)
x.i = Backing(f * (1<<Fraction_Width))
x.i &= 1<<Fraction_Width - 1
x.i |= Backing(i) << Fraction_Width
}
init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
i, f := math.modf(val);
x.i = fraction;
x.i &= 1<<Fraction_Width - 1;
x.i |= integer;
i, f := math.modf(val)
x.i = fraction
x.i &= 1<<Fraction_Width - 1
x.i |= integer
}
to_f64 :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> f64 {
res := f64(x.i >> Fraction_Width);
res += f64(x.i & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width);
return res;
res := f64(x.i >> Fraction_Width)
res += f64(x.i & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width)
return res
}
add :: proc(x, y: $T/Fixed) -> T {
return {x.i + y.i};
return {x.i + y.i}
}
sub :: proc(x, y: $T/Fixed) -> T {
return {x.i - y.i};
return {x.i - y.i}
}
mul :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_mul(x.i, y.i, Fraction_Width);
return;
z.i = intrinsics.fixed_point_mul(x.i, y.i, Fraction_Width)
return
}
mul_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_mul_sat(x.i, y.i, Fraction_Width);
return;
z.i = intrinsics.fixed_point_mul_sat(x.i, y.i, Fraction_Width)
return
}
div :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_div(x.i, y.i, Fraction_Width);
return;
z.i = intrinsics.fixed_point_div(x.i, y.i, Fraction_Width)
return
}
div_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_div_sat(x.i, y.i, Fraction_Width);
return;
z.i = intrinsics.fixed_point_div_sat(x.i, y.i, Fraction_Width)
return
}
floor :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
return x.i >> Fraction_Width;
return x.i >> Fraction_Width
}
ceil :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
Integer :: 8*size_of(Backing) - Fraction_Width;
return (x.i + (1 << Integer-1)) >> Fraction_Width;
Integer :: 8*size_of(Backing) - Fraction_Width
return (x.i + (1 << Integer-1)) >> Fraction_Width
}
round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
Integer :: 8*size_of(Backing) - Fraction_Width;
return (x.i + (1 << (Integer - 1))) >> Fraction_Width;
Integer :: 8*size_of(Backing) - Fraction_Width
return (x.i + (1 << (Integer - 1))) >> Fraction_Width
}
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
x := x;
buf: [48]byte;
i := 0;
x := x
buf: [48]byte
i := 0
if x.i < 0 {
buf[i] = '-';
i += 1;
x.i = -x.i;
buf[i] = '-'
i += 1
x.i = -x.i
}
integer := x.i >> Fraction_Width;
fraction := x.i & (1<<Fraction_Width - 1);
integer := x.i >> Fraction_Width
fraction := x.i & (1<<Fraction_Width - 1)
s := strconv.append_uint(buf[i:], u64(integer), 10);
i += len(s);
s := strconv.append_uint(buf[i:], u64(integer), 10)
i += len(s)
if fraction != 0 {
buf[i] = '.';
i += 1;
buf[i] = '.'
i += 1
for fraction > 0 {
fraction *= 10;
buf[i] = byte('0' + (fraction>>Fraction_Width));
i += 1;
fraction &= 1<<Fraction_Width - 1;
fraction *= 10
buf[i] = byte('0' + (fraction>>Fraction_Width))
i += 1
fraction &= 1<<Fraction_Width - 1
}
}
n := copy(dst, buf[:i]);
return string(dst[:i]);
n := copy(dst, buf[:i])
return string(dst[:i])
}
to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string {
buf: [48]byte;
s := append(buf[:], x);
str := make([]byte, len(s), allocator);
copy(str, s);
return string(str);
buf: [48]byte
s := append(buf[:], x)
str := make([]byte, len(s), allocator)
copy(str, s)
return string(str)
}
+170 -170
View File
@@ -6,548 +6,548 @@ import "core:math"
radians :: proc(degrees: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = degrees * RAD_PER_DEG;
out[i] = degrees * RAD_PER_DEG
}
} else {
out = degrees * RAD_PER_DEG;
out = degrees * RAD_PER_DEG
}
return;
return
}
degrees :: proc(radians: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = radians * DEG_PER_RAD;
out[i] = radians * DEG_PER_RAD
}
} else {
out = radians * DEG_PER_RAD;
out = radians * DEG_PER_RAD
}
return;
return
}
min_double :: proc(a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = builtin.min(a[i], b[i]);
out[i] = builtin.min(a[i], b[i])
}
} else {
out = builtin.min(a, b);
out = builtin.min(a, b)
}
return;
return
}
min_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
N :: len(T);
N :: len(T)
when N == 1 {
out = a[0];
out = a[0]
} else when N == 2 {
out = builtin.min(a[0], a[1]);
out = builtin.min(a[0], a[1])
} else {
out = builtin.min(a[0], a[1]);
out = builtin.min(a[0], a[1])
for i in 2..<N {
out = builtin.min(out, a[i]);
out = builtin.min(out, a[i])
}
}
} else {
out = a;
out = a
}
return;
return
}
min_triple :: proc(a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
return min_double(a, min_double(b, c));
return min_double(a, min_double(b, c))
}
min :: proc{min_single, min_double, min_triple};
min :: proc{min_single, min_double, min_triple}
max_double :: proc(a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = builtin.max(a[i], b[i]);
out[i] = builtin.max(a[i], b[i])
}
} else {
out = builtin.max(a, b);
out = builtin.max(a, b)
}
return;
return
}
max_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
N :: len(T);
N :: len(T)
when N == 1 {
out = a[0];
out = a[0]
} else when N == 2 {
out = builtin.max(a[0], a[1]);
out = builtin.max(a[0], a[1])
} else when N == 3 {
out = builtin.max(a[0], a[1], a[3]);
out = builtin.max(a[0], a[1], a[3])
}else {
out = builtin.max(a[0], a[1]);
out = builtin.max(a[0], a[1])
for i in 2..<N {
out = builtin.max(out, a[i]);
out = builtin.max(out, a[i])
}
}
} else {
out = a;
out = a
}
return;
return
}
max_triple :: proc(a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
return max_double(a, max_double(b, c));
return max_double(a, max_double(b, c))
}
max :: proc{max_single, max_double, max_triple};
max :: proc{max_single, max_double, max_triple}
abs :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = builtin.abs(a[i]);
out[i] = builtin.abs(a[i])
}
} else {
out = builtin.abs(a);
out = builtin.abs(a)
}
return;
return
}
sign :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = #force_inline math.sign(a[i]);
out[i] = #force_inline math.sign(a[i])
}
} else {
out = #force_inline math.sign(a);
out = #force_inline math.sign(a)
}
return;
return
}
clamp :: proc(x, a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = builtin.clamp(x[i], a[i], b[i]);
out[i] = builtin.clamp(x[i], a[i], b[i])
}
} else {
out = builtin.clamp(x, a, b);
out = builtin.clamp(x, a, b)
}
return;
return
}
saturate :: proc(x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
return clamp(x, 0.0, 1.0);
return clamp(x, 0.0, 1.0)
}
lerp :: proc(a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = a[i]*(1-t[i]) + b[i]*t[i];
out[i] = a[i]*(1-t[i]) + b[i]*t[i]
}
} else {
out = a * (1.0 - t) + b * t;
out = a * (1.0 - t) + b * t
}
return;
return
}
mix :: proc(a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = a[i]*(1-t[i]) + b[i]*t[i];
out[i] = a[i]*(1-t[i]) + b[i]*t[i]
}
} else {
out = a * (1.0 - t) + b * t;
out = a * (1.0 - t) + b * t
}
return;
return
}
unlerp :: proc(a, b, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
return (x - a) / (b - a);
return (x - a) / (b - a)
}
step :: proc(e, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = x[i] < e[i] ? 0.0 : 1.0;
out[i] = x[i] < e[i] ? 0.0 : 1.0
}
} else {
out = x < e ? 0.0 : 1.0;
out = x < e ? 0.0 : 1.0
}
return;
return
}
smoothstep :: proc(e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
t := saturate(unlerp(e0, e1, x));
return t * t * (3.0 - 2.0 * t);
t := saturate(unlerp(e0, e1, x))
return t * t * (3.0 - 2.0 * t)
}
smootherstep :: proc(e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
t := saturate(unlerp(e0, e1, x));
return t * t * t * (t * (6*t - 15) + 10);
t := saturate(unlerp(e0, e1, x))
return t * t * t * (t * (6*t - 15) + 10)
}
sqrt :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.sqrt(x[i]);
out[i] = math.sqrt(x[i])
}
} else {
out = math.sqrt(x);
out = math.sqrt(x)
}
return;
return
}
inverse_sqrt :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = 1.0/math.sqrt(x[i]);
out[i] = 1.0/math.sqrt(x[i])
}
} else {
out = 1.0/math.sqrt(x);
out = 1.0/math.sqrt(x)
}
return;
return
}
cos :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.cos(x[i]);
out[i] = math.cos(x[i])
}
} else {
out = math.cos(x);
out = math.cos(x)
}
return;
return
}
sin :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.sin(x[i]);
out[i] = math.sin(x[i])
}
} else {
out = math.sin(x);
out = math.sin(x)
}
return;
return
}
tan :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.tan(x[i]);
out[i] = math.tan(x[i])
}
} else {
out = math.tan(x);
out = math.tan(x)
}
return;
return
}
acos :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.acos(x[i]);
out[i] = math.acos(x[i])
}
} else {
out = math.acos(x);
out = math.acos(x)
}
return;
return
}
asin :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.asin(x[i]);
out[i] = math.asin(x[i])
}
} else {
out = math.asin(x);
out = math.asin(x)
}
return;
return
}
atan :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.atan(x[i]);
out[i] = math.atan(x[i])
}
} else {
out = math.atan(x);
out = math.atan(x)
}
return;
return
}
atan2 :: proc(y, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.atan2(y[i], x[i]);
out[i] = math.atan2(y[i], x[i])
}
} else {
out = math.atan2(y, x);
out = math.atan2(y, x)
}
return;
return
}
ln :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.ln(x[i]);
out[i] = math.ln(x[i])
}
} else {
out = math.ln(x);
out = math.ln(x)
}
return;
return
}
log2 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = INVLN2 * math.ln(x[i]);
out[i] = INVLN2 * math.ln(x[i])
}
} else {
out = INVLN2 * math.ln(x);
out = INVLN2 * math.ln(x)
}
return;
return
}
log10 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = INVLN10 * math.ln(x[i]);
out[i] = INVLN10 * math.ln(x[i])
}
} else {
out = INVLN10 * math.ln(x);
out = INVLN10 * math.ln(x)
}
return;
return
}
log :: proc(x, b: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.ln(x[i]) / math.ln(cast(ELEM_TYPE(T))b[i]);
out[i] = math.ln(x[i]) / math.ln(cast(ELEM_TYPE(T))b[i])
}
} else {
out = INVLN10 * math.ln(x) / math.ln(cast(ELEM_TYPE(T))b);
out = INVLN10 * math.ln(x) / math.ln(cast(ELEM_TYPE(T))b)
}
return;
return
}
exp :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.exp(x[i]);
out[i] = math.exp(x[i])
}
} else {
out = math.exp(x);
out = math.exp(x)
}
return;
return
}
exp2 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.exp(LN2 * x[i]);
out[i] = math.exp(LN2 * x[i])
}
} else {
out = math.exp(LN2 * x);
out = math.exp(LN2 * x)
}
return;
return
}
exp10 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.exp(LN10 * x[i]);
out[i] = math.exp(LN10 * x[i])
}
} else {
out = math.exp(LN10 * x);
out = math.exp(LN10 * x)
}
return;
return
}
pow :: proc(x, e: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.pow(x[i], e[i]);
out[i] = math.pow(x[i], e[i])
}
} else {
out = math.pow(x, e);
out = math.pow(x, e)
}
return;
return
}
ceil :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = #force_inline math.ceil(x[i]);
out[i] = #force_inline math.ceil(x[i])
}
} else {
out = #force_inline math.ceil(x);
out = #force_inline math.ceil(x)
}
return;
return
}
floor :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = #force_inline math.floor(x[i]);
out[i] = #force_inline math.floor(x[i])
}
} else {
out = #force_inline math.floor(x);
out = #force_inline math.floor(x)
}
return;
return
}
round :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = #force_inline math.round(x[i]);
out[i] = #force_inline math.round(x[i])
}
} else {
out = #force_inline math.round(x);
out = #force_inline math.round(x)
}
return;
return
}
fract :: proc(x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
f := #force_inline floor(x);
return x - f;
f := #force_inline floor(x)
return x - f
}
mod :: proc(x, m: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
f := #force_inline floor(x / m);
return x - f * m;
f := #force_inline floor(x / m)
return x - f * m
}
face_forward :: proc(N, I, N_ref: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
return dot(N_ref, I) < 0 ? N : -N;
return dot(N_ref, I) < 0 ? N : -N
}
distance :: proc(p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) {
return length(p1 - p0);
return length(p1 - p0)
}
reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
b := n * (2 * dot(n, i));
return i - b;
b := n * (2 * dot(n, i))
return i - b
}
refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
dv := dot(n, i);
k := 1 - eta*eta - (1 - dv*dv);
a := i * eta;
b := n * eta*dv*math.sqrt(k);
return (a - b) * E(int(k >= 0));
dv := dot(n, i)
k := 1 - eta*eta - (1 - dv*dv)
a := i * eta
b := n * eta*dv*math.sqrt(k)
return (a - b) * E(int(k >= 0))
}
is_nan_single :: proc(x: $T) -> bool where IS_FLOAT(T) {
return #force_inline math.is_nan(x);
return #force_inline math.is_nan(x)
}
is_nan_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
for i in 0..<N {
out[i] = #force_inline is_nan(x[i]);
out[i] = #force_inline is_nan(x[i])
}
return;
return
}
is_inf_single :: proc(x: $T) -> bool where IS_FLOAT(T) {
return #force_inline math.is_inf(x);
return #force_inline math.is_inf(x)
}
is_inf_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
for i in 0..<N {
out[i] = #force_inline is_inf(x[i]);
out[i] = #force_inline is_inf(x[i])
}
return;
return
}
classify_single :: proc(x: $T) -> math.Float_Class where IS_FLOAT(T) {
return #force_inline math.classify(x);
return #force_inline math.classify(x)
}
classify_array :: proc(x: $A/[$N]$T) -> (out: [N]math.Float_Class) where IS_FLOAT(T) {
for i in 0..<N {
out[i] = #force_inline classify_single(x[i]);
out[i] = #force_inline classify_single(x[i])
}
return;
return
}
is_nan :: proc{is_nan_single, is_nan_array};
is_inf :: proc{is_inf_single, is_inf_array};
classify :: proc{classify_single, classify_array};
is_nan :: proc{is_nan_single, is_nan_array}
is_inf :: proc{is_inf_single, is_inf_array}
classify :: proc{classify_single, classify_array}
less_than_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x < y; }
less_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x <= y; }
greater_than_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x > y; }
greater_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x >= y; }
equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x == y; }
not_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x != y; }
less_than_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x < y }
less_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x <= y }
greater_than_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x > y }
greater_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x >= y }
equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x == y }
not_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x != y }
less_than_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] < y[i];
out[i] = x[i] < y[i]
}
return;
return
}
less_than_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] <= y[i];
out[i] = x[i] <= y[i]
}
return;
return
}
greater_than_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] > y[i];
out[i] = x[i] > y[i]
}
return;
return
}
greater_than_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] >= y[i];
out[i] = x[i] >= y[i]
}
return;
return
}
equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] == y[i];
out[i] = x[i] == y[i]
}
return;
return
}
not_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] != y[i];
out[i] = x[i] != y[i]
}
return;
return
}
less_than :: proc{less_than_single, less_than_array};
less_than_equal :: proc{less_than_equal_single, less_than_equal_array};
greater_than :: proc{greater_than_single, greater_than_array};
greater_than_equal :: proc{greater_than_equal_single, greater_than_equal_array};
equal :: proc{equal_single, equal_array};
not_equal :: proc{not_equal_single, not_equal_array};
less_than :: proc{less_than_single, less_than_array}
less_than_equal :: proc{less_than_equal_single, less_than_equal_array}
greater_than :: proc{greater_than_single, greater_than_array}
greater_than_equal :: proc{greater_than_equal_single, greater_than_equal_array}
equal :: proc{equal_single, equal_array}
not_equal :: proc{not_equal_single, not_equal_array}
any :: proc(x: $A/[$N]bool) -> (out: bool) {
for e in x {
if x {
return true;
return true
}
}
return false;
return false
}
all :: proc(x: $A/[$N]bool) -> (out: bool) {
for e in x {
if !e {
return false;
return false
}
}
return true;
return true
}
not :: proc(x: $A/[$N]bool) -> (out: A) {
for e, i in x {
out[i] = !e;
out[i] = !e
}
return;
return
}
+140 -140
View File
@@ -5,178 +5,178 @@ import "core:intrinsics"
// Generic
TAU :: 6.28318530717958647692528676655900576;
PI :: 3.14159265358979323846264338327950288;
TAU :: 6.28318530717958647692528676655900576
PI :: 3.14159265358979323846264338327950288
E :: 2.71828182845904523536;
E :: 2.71828182845904523536
τ :: TAU;
π :: PI;
e :: E;
τ :: TAU
π :: PI
e :: E
SQRT_TWO :: 1.41421356237309504880168872420969808;
SQRT_THREE :: 1.73205080756887729352744634150587236;
SQRT_FIVE :: 2.23606797749978969640917366873127623;
SQRT_TWO :: 1.41421356237309504880168872420969808
SQRT_THREE :: 1.73205080756887729352744634150587236
SQRT_FIVE :: 2.23606797749978969640917366873127623
LN2 :: 0.693147180559945309417232121458176568;
LN10 :: 2.30258509299404568401799145468436421;
LN2 :: 0.693147180559945309417232121458176568
LN10 :: 2.30258509299404568401799145468436421
MAX_F64_PRECISION :: 16; // Maximum number of meaningful digits after the decimal point for 'f64'
MAX_F32_PRECISION :: 8; // Maximum number of meaningful digits after the decimal point for 'f32'
MAX_F64_PRECISION :: 16 // Maximum number of meaningful digits after the decimal point for 'f64'
MAX_F32_PRECISION :: 8 // Maximum number of meaningful digits after the decimal point for 'f32'
RAD_PER_DEG :: TAU/360.0;
DEG_PER_RAD :: 360.0/TAU;
RAD_PER_DEG :: TAU/360.0
DEG_PER_RAD :: 360.0/TAU
@private IS_NUMERIC :: intrinsics.type_is_numeric;
@private IS_QUATERNION :: intrinsics.type_is_quaternion;
@private IS_ARRAY :: intrinsics.type_is_array;
@private IS_FLOAT :: intrinsics.type_is_float;
@private BASE_TYPE :: intrinsics.type_base_type;
@private ELEM_TYPE :: intrinsics.type_elem_type;
@private IS_NUMERIC :: intrinsics.type_is_numeric
@private IS_QUATERNION :: intrinsics.type_is_quaternion
@private IS_ARRAY :: intrinsics.type_is_array
@private IS_FLOAT :: intrinsics.type_is_float
@private BASE_TYPE :: intrinsics.type_base_type
@private ELEM_TYPE :: intrinsics.type_elem_type
scalar_dot :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
return a * b;
return a * b
}
vector_dot :: proc(a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) #no_bounds_check {
for i in 0..<N {
c += a[i] * b[i];
c += a[i] * b[i]
}
return;
return
}
quaternion64_dot :: proc(a, b: $T/quaternion64) -> (c: f16) {
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
}
quaternion128_dot :: proc(a, b: $T/quaternion128) -> (c: f32) {
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
}
quaternion256_dot :: proc(a, b: $T/quaternion256) -> (c: f64) {
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
}
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot};
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot}
inner_product :: dot;
inner_product :: dot
outer_product :: proc(a: $A/[$M]$E, b: $B/[$N]E) -> (out: [M][N]E) where IS_NUMERIC(E) #no_bounds_check {
for i in 0..<M {
for j in 0..<N {
out[i][j] = a[i]*b[j];
out[i][j] = a[i]*b[j]
}
}
return;
return
}
quaternion_inverse :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0);
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0)
}
scalar_cross :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
return a * b;
return a * b
}
vector_cross2 :: proc(a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
return a[0]*b[1] - b[0]*a[1];
return a[0]*b[1] - b[0]*a[1]
}
vector_cross3 :: proc(a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
c[0] = a[1]*b[2] - b[1]*a[2];
c[1] = a[2]*b[0] - b[2]*a[0];
c[2] = a[0]*b[1] - b[0]*a[1];
return;
c[0] = a[1]*b[2] - b[1]*a[2]
c[1] = a[2]*b[0] - b[2]*a[0]
c[2] = a[0]*b[1] - b[0]*a[1]
return
}
quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
q3.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
q3.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
q3.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
q3.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
return;
q3.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y
q3.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z
q3.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x
q3.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
return
}
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3};
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross};
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3}
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross}
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
return v / length(v);
return v / length(v)
}
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return q/abs(q);
return q/abs(q)
}
normalize :: proc{vector_normalize, quaternion_normalize};
normalize :: proc{vector_normalize, quaternion_normalize}
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
m := length(v);
return 0 if m == 0 else v/m;
m := length(v)
return 0 if m == 0 else v/m
}
quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
m := abs(q);
return 0 if m == 0 else q/m;
m := abs(q)
return 0 if m == 0 else q/m
}
normalize0 :: proc{vector_normalize0, quaternion_normalize0};
normalize0 :: proc{vector_normalize0, quaternion_normalize0}
vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
return math.sqrt(dot(v, v));
return math.sqrt(dot(v, v))
}
vector_length2 :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
return dot(v, v);
return dot(v, v)
}
quaternion_length :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return abs(q);
return abs(q)
}
quaternion_length2 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return dot(q, q);
return dot(q, q)
}
scalar_triple_product :: proc(a, b, c: $T/[$N]$E) -> E where IS_NUMERIC(E) {
// a . (b x c)
// b . (c x a)
// c . (a x b)
return dot(a, cross(b, c));
return dot(a, cross(b, c))
}
vector_triple_product :: proc(a, b, c: $T/[$N]$E) -> T where IS_NUMERIC(E) {
// a x (b x c)
// (a . c)b - (a . b)c
return cross(a, cross(b, c));
return cross(a, cross(b, c))
}
length :: proc{vector_length, quaternion_length};
length2 :: proc{vector_length2, quaternion_length2};
length :: proc{vector_length, quaternion_length}
length2 :: proc{vector_length2, quaternion_length2}
projection :: proc(x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
return dot(x, normal) / dot(normal, normal) * normal;
return dot(x, normal) / dot(normal, normal) * normal
}
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
for i in 0..<N {
m[i][i] = E(1);
m[i][i] = E(1)
}
return m;
return m
}
trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
for i in 0..<N {
tr += m[i][i];
tr += m[i][i]
}
return;
return
}
transpose :: proc(a: $T/[$N][$M]$E) -> (m: (T when N == M else [M][N]E)) #no_bounds_check {
for j in 0..<M {
for i in 0..<N {
m[j][i] = a[i][j];
m[j][i] = a[i][j]
}
}
return;
return
}
matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
@@ -184,21 +184,21 @@ matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
for i in 0..<N {
for k in 0..<N {
for j in 0..<N {
c[k][i] += a[j][i] * b[k][j];
c[k][i] += a[j][i] * b[k][j]
}
}
}
return;
return
}
matrix_comp_mul :: proc(a, b: $M/[$J][$I]$E) -> (c: M)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
for j in 0..<J {
for i in 0..<I {
c[j][i] = a[j][i] * b[j][i];
c[j][i] = a[j][i] * b[j][i]
}
}
return;
return
}
matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
@@ -206,11 +206,11 @@ matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
for k in 0..<K {
for j in 0..<J {
for i in 0..<I {
c[k][i] += a[j][i] * b[k][j];
c[k][i] += a[j][i] * b[k][j]
}
}
}
return;
return
}
@@ -218,44 +218,44 @@ matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
for i in 0..<I {
for j in 0..<J {
c[j] += a[i][j] * b[i];
c[j] += a[i][j] * b[i]
}
}
return;
return
}
quaternion_mul_quaternion :: proc(q1, q2: $Q) -> Q where IS_QUATERNION(Q) {
return q1 * q2;
return q1 * q2
}
quaternion64_mul_vector3 :: proc(q: $Q/quaternion64, v: $V/[3]$F/f16) -> V {
Raw_Quaternion :: struct {xyz: [3]f16, r: f16};
Raw_Quaternion :: struct {xyz: [3]f16, r: f16}
q := transmute(Raw_Quaternion)q;
v := transmute([3]f16)v;
q := transmute(Raw_Quaternion)q
v := transmute([3]f16)v
t := cross(2*q.xyz, v);
return V(v + q.r*t + cross(q.xyz, t));
t := cross(2*q.xyz, v)
return V(v + q.r*t + cross(q.xyz, t))
}
quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
Raw_Quaternion :: struct {xyz: [3]f32, r: f32};
Raw_Quaternion :: struct {xyz: [3]f32, r: f32}
q := transmute(Raw_Quaternion)q;
v := transmute([3]f32)v;
q := transmute(Raw_Quaternion)q
v := transmute([3]f32)v
t := cross(2*q.xyz, v);
return V(v + q.r*t + cross(q.xyz, t));
t := cross(2*q.xyz, v)
return V(v + q.r*t + cross(q.xyz, t))
}
quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
Raw_Quaternion :: struct {xyz: [3]f64, r: f64};
Raw_Quaternion :: struct {xyz: [3]f64, r: f64}
q := transmute(Raw_Quaternion)q;
v := transmute([3]f64)v;
q := transmute(Raw_Quaternion)q
v := transmute([3]f64)v
t := cross(2*q.xyz, v);
return V(v + q.r*t + cross(q.xyz, t));
t := cross(2*q.xyz, v)
return V(v + q.r*t + cross(q.xyz, t))
}
quaternion_mul_vector3 :: proc{quaternion64_mul_vector3, quaternion128_mul_vector3, quaternion256_mul_vector3};
quaternion_mul_vector3 :: proc{quaternion64_mul_vector3, quaternion128_mul_vector3, quaternion256_mul_vector3}
mul :: proc{
matrix_mul,
@@ -265,16 +265,16 @@ mul :: proc{
quaternion128_mul_vector3,
quaternion256_mul_vector3,
quaternion_mul_quaternion,
};
}
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E), N > 0 #no_bounds_check {
return &v[0];
return &v[0]
}
matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0 #no_bounds_check {
return &m[0][0];
return &m[0][0]
}
to_ptr :: proc{vector_to_ptr, matrix_to_ptr};
to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
@@ -283,80 +283,80 @@ to_ptr :: proc{vector_to_ptr, matrix_to_ptr};
// Splines
vector_slerp :: proc(x, y: $T/[$N]$E, a: E) -> T {
cos_alpha := dot(x, y);
alpha := math.acos(cos_alpha);
sin_alpha := math.sin(alpha);
cos_alpha := dot(x, y)
alpha := math.acos(cos_alpha)
sin_alpha := math.sin(alpha)
t1 := math.sin((1 - a) * alpha) / sin_alpha;
t2 := math.sin(a * alpha) / sin_alpha;
t1 := math.sin((1 - a) * alpha) / sin_alpha
t2 := math.sin(a * alpha) / sin_alpha
return x * t1 + y * t2;
return x * t1 + y * t2
}
catmull_rom :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
s2 := s*s;
s3 := s2*s;
s2 := s*s
s3 := s2*s
f1 := -s3 + 2 * s2 - s;
f2 := 3 * s3 - 5 * s2 + 2;
f3 := -3 * s3 + 4 * s2 + s;
f4 := s3 - s2;
f1 := -s3 + 2 * s2 - s
f2 := 3 * s3 - 5 * s2 + 2
f3 := -3 * s3 + 4 * s2 + s
f4 := s3 - s2
return (f1 * v1 + f2 * v2 + f3 * v3 + f4 * v4) * 0.5;
return (f1 * v1 + f2 * v2 + f3 * v3 + f4 * v4) * 0.5
}
hermite :: proc(v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
s2 := s*s;
s3 := s2*s;
s2 := s*s
s3 := s2*s
f1 := 2 * s3 - 3 * s2 + 1;
f2 := -2 * s3 + 3 * s2;
f3 := s3 - 2 * s2 + s;
f4 := s3 - s2;
f1 := 2 * s3 - 3 * s2 + 1
f2 := -2 * s3 + 3 * s2
f3 := s3 - 2 * s2 + s
f4 := s3 - s2
return f1 * v1 + f2 * v2 + f3 * t1 + f4 * t2;
return f1 * v1 + f2 * v2 + f3 * t1 + f4 * t2
}
cubic :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
return ((v1 * s + v2) * s + v3) * s + v4;
return ((v1 * s + v2) * s + v3) * s + v4
}
array_cast :: proc(v: $A/[$N]$T, $Elem_Type: typeid) -> (w: [N]Elem_Type) #no_bounds_check {
for i in 0..<N {
w[i] = Elem_Type(v[i]);
w[i] = Elem_Type(v[i])
}
return;
return
}
matrix_cast :: proc(v: $A/[$M][$N]$T, $Elem_Type: typeid) -> (w: [M][N]Elem_Type) #no_bounds_check {
for i in 0..<M {
for j in 0..<N {
w[i][j] = Elem_Type(v[i][j]);
w[i][j] = Elem_Type(v[i][j])
}
}
return;
return
}
to_f32 :: #force_inline proc(v: $A/[$N]$T) -> [N]f32 { return array_cast(v, f32); }
to_f64 :: #force_inline proc(v: $A/[$N]$T) -> [N]f64 { return array_cast(v, f64); }
to_f32 :: #force_inline proc(v: $A/[$N]$T) -> [N]f32 { return array_cast(v, f32) }
to_f64 :: #force_inline proc(v: $A/[$N]$T) -> [N]f64 { return array_cast(v, f64) }
to_i8 :: #force_inline proc(v: $A/[$N]$T) -> [N]i8 { return array_cast(v, i8); }
to_i16 :: #force_inline proc(v: $A/[$N]$T) -> [N]i16 { return array_cast(v, i16); }
to_i32 :: #force_inline proc(v: $A/[$N]$T) -> [N]i32 { return array_cast(v, i32); }
to_i64 :: #force_inline proc(v: $A/[$N]$T) -> [N]i64 { return array_cast(v, i64); }
to_int :: #force_inline proc(v: $A/[$N]$T) -> [N]int { return array_cast(v, int); }
to_i8 :: #force_inline proc(v: $A/[$N]$T) -> [N]i8 { return array_cast(v, i8) }
to_i16 :: #force_inline proc(v: $A/[$N]$T) -> [N]i16 { return array_cast(v, i16) }
to_i32 :: #force_inline proc(v: $A/[$N]$T) -> [N]i32 { return array_cast(v, i32) }
to_i64 :: #force_inline proc(v: $A/[$N]$T) -> [N]i64 { return array_cast(v, i64) }
to_int :: #force_inline proc(v: $A/[$N]$T) -> [N]int { return array_cast(v, int) }
to_u8 :: #force_inline proc(v: $A/[$N]$T) -> [N]u8 { return array_cast(v, u8); }
to_u16 :: #force_inline proc(v: $A/[$N]$T) -> [N]u16 { return array_cast(v, u16); }
to_u32 :: #force_inline proc(v: $A/[$N]$T) -> [N]u32 { return array_cast(v, u32); }
to_u64 :: #force_inline proc(v: $A/[$N]$T) -> [N]u64 { return array_cast(v, u64); }
to_uint :: #force_inline proc(v: $A/[$N]$T) -> [N]uint { return array_cast(v, uint); }
to_u8 :: #force_inline proc(v: $A/[$N]$T) -> [N]u8 { return array_cast(v, u8) }
to_u16 :: #force_inline proc(v: $A/[$N]$T) -> [N]u16 { return array_cast(v, u16) }
to_u32 :: #force_inline proc(v: $A/[$N]$T) -> [N]u32 { return array_cast(v, u32) }
to_u64 :: #force_inline proc(v: $A/[$N]$T) -> [N]u64 { return array_cast(v, u64) }
to_uint :: #force_inline proc(v: $A/[$N]$T) -> [N]uint { return array_cast(v, uint) }
to_complex32 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex32 { return array_cast(v, complex32); }
to_complex64 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex64 { return array_cast(v, complex64); }
to_complex128 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex128 { return array_cast(v, complex128); }
to_quaternion64 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion64 { return array_cast(v, quaternion64); }
to_quaternion128 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion128 { return array_cast(v, quaternion128); }
to_quaternion256 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion256 { return array_cast(v, quaternion256); }
to_complex32 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex32 { return array_cast(v, complex32) }
to_complex64 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex64 { return array_cast(v, complex64) }
to_complex128 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex128 { return array_cast(v, complex128) }
to_quaternion64 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion64 { return array_cast(v, quaternion64) }
to_quaternion128 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion128 { return array_cast(v, quaternion128) }
to_quaternion256 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion256 { return array_cast(v, quaternion256) }
File diff suppressed because it is too large Load Diff
+100 -100
View File
@@ -19,109 +19,109 @@ Euler_Angle_Order :: enum {
}
quaternion_from_euler_angles :: proc{quaternion_from_euler_angles_f16, quaternion_from_euler_angles_f32, quaternion_from_euler_angles_f64};
quaternion_from_euler_angle_x :: proc{quaternion_from_euler_angle_x_f16, quaternion_from_euler_angle_x_f32, quaternion_from_euler_angle_x_f64};
quaternion_from_euler_angle_y :: proc{quaternion_from_euler_angle_y_f16, quaternion_from_euler_angle_y_f32, quaternion_from_euler_angle_y_f64};
quaternion_from_euler_angle_z :: proc{quaternion_from_euler_angle_z_f16, quaternion_from_euler_angle_z_f32, quaternion_from_euler_angle_z_f64};
quaternion_from_pitch_yaw_roll :: proc{quaternion_from_pitch_yaw_roll_f16, quaternion_from_pitch_yaw_roll_f32, quaternion_from_pitch_yaw_roll_f64};
quaternion_from_euler_angles :: proc{quaternion_from_euler_angles_f16, quaternion_from_euler_angles_f32, quaternion_from_euler_angles_f64}
quaternion_from_euler_angle_x :: proc{quaternion_from_euler_angle_x_f16, quaternion_from_euler_angle_x_f32, quaternion_from_euler_angle_x_f64}
quaternion_from_euler_angle_y :: proc{quaternion_from_euler_angle_y_f16, quaternion_from_euler_angle_y_f32, quaternion_from_euler_angle_y_f64}
quaternion_from_euler_angle_z :: proc{quaternion_from_euler_angle_z_f16, quaternion_from_euler_angle_z_f32, quaternion_from_euler_angle_z_f64}
quaternion_from_pitch_yaw_roll :: proc{quaternion_from_pitch_yaw_roll_f16, quaternion_from_pitch_yaw_roll_f32, quaternion_from_pitch_yaw_roll_f64}
euler_angles_from_quaternion :: proc{euler_angles_from_quaternion_f16, euler_angles_from_quaternion_f32, euler_angles_from_quaternion_f64};
euler_angles_xyz_from_quaternion :: proc{euler_angles_xyz_from_quaternion_f16, euler_angles_xyz_from_quaternion_f32, euler_angles_xyz_from_quaternion_f64};
euler_angles_yxz_from_quaternion :: proc{euler_angles_yxz_from_quaternion_f16, euler_angles_yxz_from_quaternion_f32, euler_angles_yxz_from_quaternion_f64};
euler_angles_xzx_from_quaternion :: proc{euler_angles_xzx_from_quaternion_f16, euler_angles_xzx_from_quaternion_f32, euler_angles_xzx_from_quaternion_f64};
euler_angles_xyx_from_quaternion :: proc{euler_angles_xyx_from_quaternion_f16, euler_angles_xyx_from_quaternion_f32, euler_angles_xyx_from_quaternion_f64};
euler_angles_yxy_from_quaternion :: proc{euler_angles_yxy_from_quaternion_f16, euler_angles_yxy_from_quaternion_f32, euler_angles_yxy_from_quaternion_f64};
euler_angles_yzy_from_quaternion :: proc{euler_angles_yzy_from_quaternion_f16, euler_angles_yzy_from_quaternion_f32, euler_angles_yzy_from_quaternion_f64};
euler_angles_zyz_from_quaternion :: proc{euler_angles_zyz_from_quaternion_f16, euler_angles_zyz_from_quaternion_f32, euler_angles_zyz_from_quaternion_f64};
euler_angles_zxz_from_quaternion :: proc{euler_angles_zxz_from_quaternion_f16, euler_angles_zxz_from_quaternion_f32, euler_angles_zxz_from_quaternion_f64};
euler_angles_xzy_from_quaternion :: proc{euler_angles_xzy_from_quaternion_f16, euler_angles_xzy_from_quaternion_f32, euler_angles_xzy_from_quaternion_f64};
euler_angles_yzx_from_quaternion :: proc{euler_angles_yzx_from_quaternion_f16, euler_angles_yzx_from_quaternion_f32, euler_angles_yzx_from_quaternion_f64};
euler_angles_zyx_from_quaternion :: proc{euler_angles_zyx_from_quaternion_f16, euler_angles_zyx_from_quaternion_f32, euler_angles_zyx_from_quaternion_f64};
euler_angles_zxy_from_quaternion :: proc{euler_angles_zxy_from_quaternion_f16, euler_angles_zxy_from_quaternion_f32, euler_angles_zxy_from_quaternion_f64};
euler_angles_from_quaternion :: proc{euler_angles_from_quaternion_f16, euler_angles_from_quaternion_f32, euler_angles_from_quaternion_f64}
euler_angles_xyz_from_quaternion :: proc{euler_angles_xyz_from_quaternion_f16, euler_angles_xyz_from_quaternion_f32, euler_angles_xyz_from_quaternion_f64}
euler_angles_yxz_from_quaternion :: proc{euler_angles_yxz_from_quaternion_f16, euler_angles_yxz_from_quaternion_f32, euler_angles_yxz_from_quaternion_f64}
euler_angles_xzx_from_quaternion :: proc{euler_angles_xzx_from_quaternion_f16, euler_angles_xzx_from_quaternion_f32, euler_angles_xzx_from_quaternion_f64}
euler_angles_xyx_from_quaternion :: proc{euler_angles_xyx_from_quaternion_f16, euler_angles_xyx_from_quaternion_f32, euler_angles_xyx_from_quaternion_f64}
euler_angles_yxy_from_quaternion :: proc{euler_angles_yxy_from_quaternion_f16, euler_angles_yxy_from_quaternion_f32, euler_angles_yxy_from_quaternion_f64}
euler_angles_yzy_from_quaternion :: proc{euler_angles_yzy_from_quaternion_f16, euler_angles_yzy_from_quaternion_f32, euler_angles_yzy_from_quaternion_f64}
euler_angles_zyz_from_quaternion :: proc{euler_angles_zyz_from_quaternion_f16, euler_angles_zyz_from_quaternion_f32, euler_angles_zyz_from_quaternion_f64}
euler_angles_zxz_from_quaternion :: proc{euler_angles_zxz_from_quaternion_f16, euler_angles_zxz_from_quaternion_f32, euler_angles_zxz_from_quaternion_f64}
euler_angles_xzy_from_quaternion :: proc{euler_angles_xzy_from_quaternion_f16, euler_angles_xzy_from_quaternion_f32, euler_angles_xzy_from_quaternion_f64}
euler_angles_yzx_from_quaternion :: proc{euler_angles_yzx_from_quaternion_f16, euler_angles_yzx_from_quaternion_f32, euler_angles_yzx_from_quaternion_f64}
euler_angles_zyx_from_quaternion :: proc{euler_angles_zyx_from_quaternion_f16, euler_angles_zyx_from_quaternion_f32, euler_angles_zyx_from_quaternion_f64}
euler_angles_zxy_from_quaternion :: proc{euler_angles_zxy_from_quaternion_f16, euler_angles_zxy_from_quaternion_f32, euler_angles_zxy_from_quaternion_f64}
roll_from_quaternion :: proc{roll_from_quaternion_f16, roll_from_quaternion_f32, roll_from_quaternion_f64};
pitch_from_quaternion :: proc{pitch_from_quaternion_f16, pitch_from_quaternion_f32, pitch_from_quaternion_f64};
yaw_from_quaternion :: proc{yaw_from_quaternion_f16, yaw_from_quaternion_f32, yaw_from_quaternion_f64};
pitch_yaw_roll_from_quaternion :: proc{pitch_yaw_roll_from_quaternion_f16, pitch_yaw_roll_from_quaternion_f32, pitch_yaw_roll_from_quaternion_f64};
roll_from_quaternion :: proc{roll_from_quaternion_f16, roll_from_quaternion_f32, roll_from_quaternion_f64}
pitch_from_quaternion :: proc{pitch_from_quaternion_f16, pitch_from_quaternion_f32, pitch_from_quaternion_f64}
yaw_from_quaternion :: proc{yaw_from_quaternion_f16, yaw_from_quaternion_f32, yaw_from_quaternion_f64}
pitch_yaw_roll_from_quaternion :: proc{pitch_yaw_roll_from_quaternion_f16, pitch_yaw_roll_from_quaternion_f32, pitch_yaw_roll_from_quaternion_f64}
matrix3_from_euler_angles :: proc{matrix3_from_euler_angles_f16, matrix3_from_euler_angles_f32, matrix3_from_euler_angles_f64};
matrix3_from_euler_angle_x :: proc{matrix3_from_euler_angle_x_f16, matrix3_from_euler_angle_x_f32, matrix3_from_euler_angle_x_f64};
matrix3_from_euler_angle_y :: proc{matrix3_from_euler_angle_y_f16, matrix3_from_euler_angle_y_f32, matrix3_from_euler_angle_y_f64};
matrix3_from_euler_angle_z :: proc{matrix3_from_euler_angle_z_f16, matrix3_from_euler_angle_z_f32, matrix3_from_euler_angle_z_f64};
matrix3_from_derived_euler_angle_x :: proc{matrix3_from_derived_euler_angle_x_f16, matrix3_from_derived_euler_angle_x_f32, matrix3_from_derived_euler_angle_x_f64};
matrix3_from_derived_euler_angle_y :: proc{matrix3_from_derived_euler_angle_y_f16, matrix3_from_derived_euler_angle_y_f32, matrix3_from_derived_euler_angle_y_f64};
matrix3_from_derived_euler_angle_z :: proc{matrix3_from_derived_euler_angle_z_f16, matrix3_from_derived_euler_angle_z_f32, matrix3_from_derived_euler_angle_z_f64};
matrix3_from_euler_angles_xy :: proc{matrix3_from_euler_angles_xy_f16, matrix3_from_euler_angles_xy_f32, matrix3_from_euler_angles_xy_f64};
matrix3_from_euler_angles_yx :: proc{matrix3_from_euler_angles_yx_f16, matrix3_from_euler_angles_yx_f32, matrix3_from_euler_angles_yx_f64};
matrix3_from_euler_angles_xz :: proc{matrix3_from_euler_angles_xz_f16, matrix3_from_euler_angles_xz_f32, matrix3_from_euler_angles_xz_f64};
matrix3_from_euler_angles_zx :: proc{matrix3_from_euler_angles_zx_f16, matrix3_from_euler_angles_zx_f32, matrix3_from_euler_angles_zx_f64};
matrix3_from_euler_angles_yz :: proc{matrix3_from_euler_angles_yz_f16, matrix3_from_euler_angles_yz_f32, matrix3_from_euler_angles_yz_f64};
matrix3_from_euler_angles_zy :: proc{matrix3_from_euler_angles_zy_f16, matrix3_from_euler_angles_zy_f32, matrix3_from_euler_angles_zy_f64};
matrix3_from_euler_angles_xyz :: proc{matrix3_from_euler_angles_xyz_f16, matrix3_from_euler_angles_xyz_f32, matrix3_from_euler_angles_xyz_f64};
matrix3_from_euler_angles_yxz :: proc{matrix3_from_euler_angles_yxz_f16, matrix3_from_euler_angles_yxz_f32, matrix3_from_euler_angles_yxz_f64};
matrix3_from_euler_angles_xzx :: proc{matrix3_from_euler_angles_xzx_f16, matrix3_from_euler_angles_xzx_f32, matrix3_from_euler_angles_xzx_f64};
matrix3_from_euler_angles_xyx :: proc{matrix3_from_euler_angles_xyx_f16, matrix3_from_euler_angles_xyx_f32, matrix3_from_euler_angles_xyx_f64};
matrix3_from_euler_angles_yxy :: proc{matrix3_from_euler_angles_yxy_f16, matrix3_from_euler_angles_yxy_f32, matrix3_from_euler_angles_yxy_f64};
matrix3_from_euler_angles_yzy :: proc{matrix3_from_euler_angles_yzy_f16, matrix3_from_euler_angles_yzy_f32, matrix3_from_euler_angles_yzy_f64};
matrix3_from_euler_angles_zyz :: proc{matrix3_from_euler_angles_zyz_f16, matrix3_from_euler_angles_zyz_f32, matrix3_from_euler_angles_zyz_f64};
matrix3_from_euler_angles_zxz :: proc{matrix3_from_euler_angles_zxz_f16, matrix3_from_euler_angles_zxz_f32, matrix3_from_euler_angles_zxz_f64};
matrix3_from_euler_angles_xzy :: proc{matrix3_from_euler_angles_xzy_f16, matrix3_from_euler_angles_xzy_f32, matrix3_from_euler_angles_xzy_f64};
matrix3_from_euler_angles_yzx :: proc{matrix3_from_euler_angles_yzx_f16, matrix3_from_euler_angles_yzx_f32, matrix3_from_euler_angles_yzx_f64};
matrix3_from_euler_angles_zyx :: proc{matrix3_from_euler_angles_zyx_f16, matrix3_from_euler_angles_zyx_f32, matrix3_from_euler_angles_zyx_f64};
matrix3_from_euler_angles_zxy :: proc{matrix3_from_euler_angles_zxy_f16, matrix3_from_euler_angles_zxy_f32, matrix3_from_euler_angles_zxy_f64};
matrix3_from_yaw_pitch_roll :: proc{matrix3_from_yaw_pitch_roll_f16, matrix3_from_yaw_pitch_roll_f32, matrix3_from_yaw_pitch_roll_f64};
matrix3_from_euler_angles :: proc{matrix3_from_euler_angles_f16, matrix3_from_euler_angles_f32, matrix3_from_euler_angles_f64}
matrix3_from_euler_angle_x :: proc{matrix3_from_euler_angle_x_f16, matrix3_from_euler_angle_x_f32, matrix3_from_euler_angle_x_f64}
matrix3_from_euler_angle_y :: proc{matrix3_from_euler_angle_y_f16, matrix3_from_euler_angle_y_f32, matrix3_from_euler_angle_y_f64}
matrix3_from_euler_angle_z :: proc{matrix3_from_euler_angle_z_f16, matrix3_from_euler_angle_z_f32, matrix3_from_euler_angle_z_f64}
matrix3_from_derived_euler_angle_x :: proc{matrix3_from_derived_euler_angle_x_f16, matrix3_from_derived_euler_angle_x_f32, matrix3_from_derived_euler_angle_x_f64}
matrix3_from_derived_euler_angle_y :: proc{matrix3_from_derived_euler_angle_y_f16, matrix3_from_derived_euler_angle_y_f32, matrix3_from_derived_euler_angle_y_f64}
matrix3_from_derived_euler_angle_z :: proc{matrix3_from_derived_euler_angle_z_f16, matrix3_from_derived_euler_angle_z_f32, matrix3_from_derived_euler_angle_z_f64}
matrix3_from_euler_angles_xy :: proc{matrix3_from_euler_angles_xy_f16, matrix3_from_euler_angles_xy_f32, matrix3_from_euler_angles_xy_f64}
matrix3_from_euler_angles_yx :: proc{matrix3_from_euler_angles_yx_f16, matrix3_from_euler_angles_yx_f32, matrix3_from_euler_angles_yx_f64}
matrix3_from_euler_angles_xz :: proc{matrix3_from_euler_angles_xz_f16, matrix3_from_euler_angles_xz_f32, matrix3_from_euler_angles_xz_f64}
matrix3_from_euler_angles_zx :: proc{matrix3_from_euler_angles_zx_f16, matrix3_from_euler_angles_zx_f32, matrix3_from_euler_angles_zx_f64}
matrix3_from_euler_angles_yz :: proc{matrix3_from_euler_angles_yz_f16, matrix3_from_euler_angles_yz_f32, matrix3_from_euler_angles_yz_f64}
matrix3_from_euler_angles_zy :: proc{matrix3_from_euler_angles_zy_f16, matrix3_from_euler_angles_zy_f32, matrix3_from_euler_angles_zy_f64}
matrix3_from_euler_angles_xyz :: proc{matrix3_from_euler_angles_xyz_f16, matrix3_from_euler_angles_xyz_f32, matrix3_from_euler_angles_xyz_f64}
matrix3_from_euler_angles_yxz :: proc{matrix3_from_euler_angles_yxz_f16, matrix3_from_euler_angles_yxz_f32, matrix3_from_euler_angles_yxz_f64}
matrix3_from_euler_angles_xzx :: proc{matrix3_from_euler_angles_xzx_f16, matrix3_from_euler_angles_xzx_f32, matrix3_from_euler_angles_xzx_f64}
matrix3_from_euler_angles_xyx :: proc{matrix3_from_euler_angles_xyx_f16, matrix3_from_euler_angles_xyx_f32, matrix3_from_euler_angles_xyx_f64}
matrix3_from_euler_angles_yxy :: proc{matrix3_from_euler_angles_yxy_f16, matrix3_from_euler_angles_yxy_f32, matrix3_from_euler_angles_yxy_f64}
matrix3_from_euler_angles_yzy :: proc{matrix3_from_euler_angles_yzy_f16, matrix3_from_euler_angles_yzy_f32, matrix3_from_euler_angles_yzy_f64}
matrix3_from_euler_angles_zyz :: proc{matrix3_from_euler_angles_zyz_f16, matrix3_from_euler_angles_zyz_f32, matrix3_from_euler_angles_zyz_f64}
matrix3_from_euler_angles_zxz :: proc{matrix3_from_euler_angles_zxz_f16, matrix3_from_euler_angles_zxz_f32, matrix3_from_euler_angles_zxz_f64}
matrix3_from_euler_angles_xzy :: proc{matrix3_from_euler_angles_xzy_f16, matrix3_from_euler_angles_xzy_f32, matrix3_from_euler_angles_xzy_f64}
matrix3_from_euler_angles_yzx :: proc{matrix3_from_euler_angles_yzx_f16, matrix3_from_euler_angles_yzx_f32, matrix3_from_euler_angles_yzx_f64}
matrix3_from_euler_angles_zyx :: proc{matrix3_from_euler_angles_zyx_f16, matrix3_from_euler_angles_zyx_f32, matrix3_from_euler_angles_zyx_f64}
matrix3_from_euler_angles_zxy :: proc{matrix3_from_euler_angles_zxy_f16, matrix3_from_euler_angles_zxy_f32, matrix3_from_euler_angles_zxy_f64}
matrix3_from_yaw_pitch_roll :: proc{matrix3_from_yaw_pitch_roll_f16, matrix3_from_yaw_pitch_roll_f32, matrix3_from_yaw_pitch_roll_f64}
euler_angles_from_matrix3 :: proc{euler_angles_from_matrix3_f16, euler_angles_from_matrix3_f32, euler_angles_from_matrix3_f64};
euler_angles_xyz_from_matrix3 :: proc{euler_angles_xyz_from_matrix3_f16, euler_angles_xyz_from_matrix3_f32, euler_angles_xyz_from_matrix3_f64};
euler_angles_yxz_from_matrix3 :: proc{euler_angles_yxz_from_matrix3_f16, euler_angles_yxz_from_matrix3_f32, euler_angles_yxz_from_matrix3_f64};
euler_angles_xzx_from_matrix3 :: proc{euler_angles_xzx_from_matrix3_f16, euler_angles_xzx_from_matrix3_f32, euler_angles_xzx_from_matrix3_f64};
euler_angles_xyx_from_matrix3 :: proc{euler_angles_xyx_from_matrix3_f16, euler_angles_xyx_from_matrix3_f32, euler_angles_xyx_from_matrix3_f64};
euler_angles_yxy_from_matrix3 :: proc{euler_angles_yxy_from_matrix3_f16, euler_angles_yxy_from_matrix3_f32, euler_angles_yxy_from_matrix3_f64};
euler_angles_yzy_from_matrix3 :: proc{euler_angles_yzy_from_matrix3_f16, euler_angles_yzy_from_matrix3_f32, euler_angles_yzy_from_matrix3_f64};
euler_angles_zyz_from_matrix3 :: proc{euler_angles_zyz_from_matrix3_f16, euler_angles_zyz_from_matrix3_f32, euler_angles_zyz_from_matrix3_f64};
euler_angles_zxz_from_matrix3 :: proc{euler_angles_zxz_from_matrix3_f16, euler_angles_zxz_from_matrix3_f32, euler_angles_zxz_from_matrix3_f64};
euler_angles_xzy_from_matrix3 :: proc{euler_angles_xzy_from_matrix3_f16, euler_angles_xzy_from_matrix3_f32, euler_angles_xzy_from_matrix3_f64};
euler_angles_yzx_from_matrix3 :: proc{euler_angles_yzx_from_matrix3_f16, euler_angles_yzx_from_matrix3_f32, euler_angles_yzx_from_matrix3_f64};
euler_angles_zyx_from_matrix3 :: proc{euler_angles_zyx_from_matrix3_f16, euler_angles_zyx_from_matrix3_f32, euler_angles_zyx_from_matrix3_f64};
euler_angles_zxy_from_matrix3 :: proc{euler_angles_zxy_from_matrix3_f16, euler_angles_zxy_from_matrix3_f32, euler_angles_zxy_from_matrix3_f64};
euler_angles_from_matrix3 :: proc{euler_angles_from_matrix3_f16, euler_angles_from_matrix3_f32, euler_angles_from_matrix3_f64}
euler_angles_xyz_from_matrix3 :: proc{euler_angles_xyz_from_matrix3_f16, euler_angles_xyz_from_matrix3_f32, euler_angles_xyz_from_matrix3_f64}
euler_angles_yxz_from_matrix3 :: proc{euler_angles_yxz_from_matrix3_f16, euler_angles_yxz_from_matrix3_f32, euler_angles_yxz_from_matrix3_f64}
euler_angles_xzx_from_matrix3 :: proc{euler_angles_xzx_from_matrix3_f16, euler_angles_xzx_from_matrix3_f32, euler_angles_xzx_from_matrix3_f64}
euler_angles_xyx_from_matrix3 :: proc{euler_angles_xyx_from_matrix3_f16, euler_angles_xyx_from_matrix3_f32, euler_angles_xyx_from_matrix3_f64}
euler_angles_yxy_from_matrix3 :: proc{euler_angles_yxy_from_matrix3_f16, euler_angles_yxy_from_matrix3_f32, euler_angles_yxy_from_matrix3_f64}
euler_angles_yzy_from_matrix3 :: proc{euler_angles_yzy_from_matrix3_f16, euler_angles_yzy_from_matrix3_f32, euler_angles_yzy_from_matrix3_f64}
euler_angles_zyz_from_matrix3 :: proc{euler_angles_zyz_from_matrix3_f16, euler_angles_zyz_from_matrix3_f32, euler_angles_zyz_from_matrix3_f64}
euler_angles_zxz_from_matrix3 :: proc{euler_angles_zxz_from_matrix3_f16, euler_angles_zxz_from_matrix3_f32, euler_angles_zxz_from_matrix3_f64}
euler_angles_xzy_from_matrix3 :: proc{euler_angles_xzy_from_matrix3_f16, euler_angles_xzy_from_matrix3_f32, euler_angles_xzy_from_matrix3_f64}
euler_angles_yzx_from_matrix3 :: proc{euler_angles_yzx_from_matrix3_f16, euler_angles_yzx_from_matrix3_f32, euler_angles_yzx_from_matrix3_f64}
euler_angles_zyx_from_matrix3 :: proc{euler_angles_zyx_from_matrix3_f16, euler_angles_zyx_from_matrix3_f32, euler_angles_zyx_from_matrix3_f64}
euler_angles_zxy_from_matrix3 :: proc{euler_angles_zxy_from_matrix3_f16, euler_angles_zxy_from_matrix3_f32, euler_angles_zxy_from_matrix3_f64}
matrix4_from_euler_angles :: proc{matrix4_from_euler_angles_f16, matrix4_from_euler_angles_f32, matrix4_from_euler_angles_f64};
matrix4_from_euler_angle_x :: proc{matrix4_from_euler_angle_x_f16, matrix4_from_euler_angle_x_f32, matrix4_from_euler_angle_x_f64};
matrix4_from_euler_angle_y :: proc{matrix4_from_euler_angle_y_f16, matrix4_from_euler_angle_y_f32, matrix4_from_euler_angle_y_f64};
matrix4_from_euler_angle_z :: proc{matrix4_from_euler_angle_z_f16, matrix4_from_euler_angle_z_f32, matrix4_from_euler_angle_z_f64};
matrix4_from_derived_euler_angle_x :: proc{matrix4_from_derived_euler_angle_x_f16, matrix4_from_derived_euler_angle_x_f32, matrix4_from_derived_euler_angle_x_f64};
matrix4_from_derived_euler_angle_y :: proc{matrix4_from_derived_euler_angle_y_f16, matrix4_from_derived_euler_angle_y_f32, matrix4_from_derived_euler_angle_y_f64};
matrix4_from_derived_euler_angle_z :: proc{matrix4_from_derived_euler_angle_z_f16, matrix4_from_derived_euler_angle_z_f32, matrix4_from_derived_euler_angle_z_f64};
matrix4_from_euler_angles_xy :: proc{matrix4_from_euler_angles_xy_f16, matrix4_from_euler_angles_xy_f32, matrix4_from_euler_angles_xy_f64};
matrix4_from_euler_angles_yx :: proc{matrix4_from_euler_angles_yx_f16, matrix4_from_euler_angles_yx_f32, matrix4_from_euler_angles_yx_f64};
matrix4_from_euler_angles_xz :: proc{matrix4_from_euler_angles_xz_f16, matrix4_from_euler_angles_xz_f32, matrix4_from_euler_angles_xz_f64};
matrix4_from_euler_angles_zx :: proc{matrix4_from_euler_angles_zx_f16, matrix4_from_euler_angles_zx_f32, matrix4_from_euler_angles_zx_f64};
matrix4_from_euler_angles_yz :: proc{matrix4_from_euler_angles_yz_f16, matrix4_from_euler_angles_yz_f32, matrix4_from_euler_angles_yz_f64};
matrix4_from_euler_angles_zy :: proc{matrix4_from_euler_angles_zy_f16, matrix4_from_euler_angles_zy_f32, matrix4_from_euler_angles_zy_f64};
matrix4_from_euler_angles_xyz :: proc{matrix4_from_euler_angles_xyz_f16, matrix4_from_euler_angles_xyz_f32, matrix4_from_euler_angles_xyz_f64};
matrix4_from_euler_angles_yxz :: proc{matrix4_from_euler_angles_yxz_f16, matrix4_from_euler_angles_yxz_f32, matrix4_from_euler_angles_yxz_f64};
matrix4_from_euler_angles_xzx :: proc{matrix4_from_euler_angles_xzx_f16, matrix4_from_euler_angles_xzx_f32, matrix4_from_euler_angles_xzx_f64};
matrix4_from_euler_angles_xyx :: proc{matrix4_from_euler_angles_xyx_f16, matrix4_from_euler_angles_xyx_f32, matrix4_from_euler_angles_xyx_f64};
matrix4_from_euler_angles_yxy :: proc{matrix4_from_euler_angles_yxy_f16, matrix4_from_euler_angles_yxy_f32, matrix4_from_euler_angles_yxy_f64};
matrix4_from_euler_angles_yzy :: proc{matrix4_from_euler_angles_yzy_f16, matrix4_from_euler_angles_yzy_f32, matrix4_from_euler_angles_yzy_f64};
matrix4_from_euler_angles_zyz :: proc{matrix4_from_euler_angles_zyz_f16, matrix4_from_euler_angles_zyz_f32, matrix4_from_euler_angles_zyz_f64};
matrix4_from_euler_angles_zxz :: proc{matrix4_from_euler_angles_zxz_f16, matrix4_from_euler_angles_zxz_f32, matrix4_from_euler_angles_zxz_f64};
matrix4_from_euler_angles_xzy :: proc{matrix4_from_euler_angles_xzy_f16, matrix4_from_euler_angles_xzy_f32, matrix4_from_euler_angles_xzy_f64};
matrix4_from_euler_angles_yzx :: proc{matrix4_from_euler_angles_yzx_f16, matrix4_from_euler_angles_yzx_f32, matrix4_from_euler_angles_yzx_f64};
matrix4_from_euler_angles_zyx :: proc{matrix4_from_euler_angles_zyx_f16, matrix4_from_euler_angles_zyx_f32, matrix4_from_euler_angles_zyx_f64};
matrix4_from_euler_angles_zxy :: proc{matrix4_from_euler_angles_zxy_f16, matrix4_from_euler_angles_zxy_f32, matrix4_from_euler_angles_zxy_f64};
matrix4_from_yaw_pitch_roll :: proc{matrix4_from_yaw_pitch_roll_f16, matrix4_from_yaw_pitch_roll_f32, matrix4_from_yaw_pitch_roll_f64};
matrix4_from_euler_angles :: proc{matrix4_from_euler_angles_f16, matrix4_from_euler_angles_f32, matrix4_from_euler_angles_f64}
matrix4_from_euler_angle_x :: proc{matrix4_from_euler_angle_x_f16, matrix4_from_euler_angle_x_f32, matrix4_from_euler_angle_x_f64}
matrix4_from_euler_angle_y :: proc{matrix4_from_euler_angle_y_f16, matrix4_from_euler_angle_y_f32, matrix4_from_euler_angle_y_f64}
matrix4_from_euler_angle_z :: proc{matrix4_from_euler_angle_z_f16, matrix4_from_euler_angle_z_f32, matrix4_from_euler_angle_z_f64}
matrix4_from_derived_euler_angle_x :: proc{matrix4_from_derived_euler_angle_x_f16, matrix4_from_derived_euler_angle_x_f32, matrix4_from_derived_euler_angle_x_f64}
matrix4_from_derived_euler_angle_y :: proc{matrix4_from_derived_euler_angle_y_f16, matrix4_from_derived_euler_angle_y_f32, matrix4_from_derived_euler_angle_y_f64}
matrix4_from_derived_euler_angle_z :: proc{matrix4_from_derived_euler_angle_z_f16, matrix4_from_derived_euler_angle_z_f32, matrix4_from_derived_euler_angle_z_f64}
matrix4_from_euler_angles_xy :: proc{matrix4_from_euler_angles_xy_f16, matrix4_from_euler_angles_xy_f32, matrix4_from_euler_angles_xy_f64}
matrix4_from_euler_angles_yx :: proc{matrix4_from_euler_angles_yx_f16, matrix4_from_euler_angles_yx_f32, matrix4_from_euler_angles_yx_f64}
matrix4_from_euler_angles_xz :: proc{matrix4_from_euler_angles_xz_f16, matrix4_from_euler_angles_xz_f32, matrix4_from_euler_angles_xz_f64}
matrix4_from_euler_angles_zx :: proc{matrix4_from_euler_angles_zx_f16, matrix4_from_euler_angles_zx_f32, matrix4_from_euler_angles_zx_f64}
matrix4_from_euler_angles_yz :: proc{matrix4_from_euler_angles_yz_f16, matrix4_from_euler_angles_yz_f32, matrix4_from_euler_angles_yz_f64}
matrix4_from_euler_angles_zy :: proc{matrix4_from_euler_angles_zy_f16, matrix4_from_euler_angles_zy_f32, matrix4_from_euler_angles_zy_f64}
matrix4_from_euler_angles_xyz :: proc{matrix4_from_euler_angles_xyz_f16, matrix4_from_euler_angles_xyz_f32, matrix4_from_euler_angles_xyz_f64}
matrix4_from_euler_angles_yxz :: proc{matrix4_from_euler_angles_yxz_f16, matrix4_from_euler_angles_yxz_f32, matrix4_from_euler_angles_yxz_f64}
matrix4_from_euler_angles_xzx :: proc{matrix4_from_euler_angles_xzx_f16, matrix4_from_euler_angles_xzx_f32, matrix4_from_euler_angles_xzx_f64}
matrix4_from_euler_angles_xyx :: proc{matrix4_from_euler_angles_xyx_f16, matrix4_from_euler_angles_xyx_f32, matrix4_from_euler_angles_xyx_f64}
matrix4_from_euler_angles_yxy :: proc{matrix4_from_euler_angles_yxy_f16, matrix4_from_euler_angles_yxy_f32, matrix4_from_euler_angles_yxy_f64}
matrix4_from_euler_angles_yzy :: proc{matrix4_from_euler_angles_yzy_f16, matrix4_from_euler_angles_yzy_f32, matrix4_from_euler_angles_yzy_f64}
matrix4_from_euler_angles_zyz :: proc{matrix4_from_euler_angles_zyz_f16, matrix4_from_euler_angles_zyz_f32, matrix4_from_euler_angles_zyz_f64}
matrix4_from_euler_angles_zxz :: proc{matrix4_from_euler_angles_zxz_f16, matrix4_from_euler_angles_zxz_f32, matrix4_from_euler_angles_zxz_f64}
matrix4_from_euler_angles_xzy :: proc{matrix4_from_euler_angles_xzy_f16, matrix4_from_euler_angles_xzy_f32, matrix4_from_euler_angles_xzy_f64}
matrix4_from_euler_angles_yzx :: proc{matrix4_from_euler_angles_yzx_f16, matrix4_from_euler_angles_yzx_f32, matrix4_from_euler_angles_yzx_f64}
matrix4_from_euler_angles_zyx :: proc{matrix4_from_euler_angles_zyx_f16, matrix4_from_euler_angles_zyx_f32, matrix4_from_euler_angles_zyx_f64}
matrix4_from_euler_angles_zxy :: proc{matrix4_from_euler_angles_zxy_f16, matrix4_from_euler_angles_zxy_f32, matrix4_from_euler_angles_zxy_f64}
matrix4_from_yaw_pitch_roll :: proc{matrix4_from_yaw_pitch_roll_f16, matrix4_from_yaw_pitch_roll_f32, matrix4_from_yaw_pitch_roll_f64}
euler_angles_from_matrix4 :: proc{euler_angles_from_matrix4_f16, euler_angles_from_matrix4_f32, euler_angles_from_matrix4_f64};
euler_angles_xyz_from_matrix4 :: proc{euler_angles_xyz_from_matrix4_f16, euler_angles_xyz_from_matrix4_f32, euler_angles_xyz_from_matrix4_f64};
euler_angles_yxz_from_matrix4 :: proc{euler_angles_yxz_from_matrix4_f16, euler_angles_yxz_from_matrix4_f32, euler_angles_yxz_from_matrix4_f64};
euler_angles_xzx_from_matrix4 :: proc{euler_angles_xzx_from_matrix4_f16, euler_angles_xzx_from_matrix4_f32, euler_angles_xzx_from_matrix4_f64};
euler_angles_xyx_from_matrix4 :: proc{euler_angles_xyx_from_matrix4_f16, euler_angles_xyx_from_matrix4_f32, euler_angles_xyx_from_matrix4_f64};
euler_angles_yxy_from_matrix4 :: proc{euler_angles_yxy_from_matrix4_f16, euler_angles_yxy_from_matrix4_f32, euler_angles_yxy_from_matrix4_f64};
euler_angles_yzy_from_matrix4 :: proc{euler_angles_yzy_from_matrix4_f16, euler_angles_yzy_from_matrix4_f32, euler_angles_yzy_from_matrix4_f64};
euler_angles_zyz_from_matrix4 :: proc{euler_angles_zyz_from_matrix4_f16, euler_angles_zyz_from_matrix4_f32, euler_angles_zyz_from_matrix4_f64};
euler_angles_zxz_from_matrix4 :: proc{euler_angles_zxz_from_matrix4_f16, euler_angles_zxz_from_matrix4_f32, euler_angles_zxz_from_matrix4_f64};
euler_angles_xzy_from_matrix4 :: proc{euler_angles_xzy_from_matrix4_f16, euler_angles_xzy_from_matrix4_f32, euler_angles_xzy_from_matrix4_f64};
euler_angles_yzx_from_matrix4 :: proc{euler_angles_yzx_from_matrix4_f16, euler_angles_yzx_from_matrix4_f32, euler_angles_yzx_from_matrix4_f64};
euler_angles_zyx_from_matrix4 :: proc{euler_angles_zyx_from_matrix4_f16, euler_angles_zyx_from_matrix4_f32, euler_angles_zyx_from_matrix4_f64};
euler_angles_zxy_from_matrix4 :: proc{euler_angles_zxy_from_matrix4_f16, euler_angles_zxy_from_matrix4_f32, euler_angles_zxy_from_matrix4_f64};
euler_angles_from_matrix4 :: proc{euler_angles_from_matrix4_f16, euler_angles_from_matrix4_f32, euler_angles_from_matrix4_f64}
euler_angles_xyz_from_matrix4 :: proc{euler_angles_xyz_from_matrix4_f16, euler_angles_xyz_from_matrix4_f32, euler_angles_xyz_from_matrix4_f64}
euler_angles_yxz_from_matrix4 :: proc{euler_angles_yxz_from_matrix4_f16, euler_angles_yxz_from_matrix4_f32, euler_angles_yxz_from_matrix4_f64}
euler_angles_xzx_from_matrix4 :: proc{euler_angles_xzx_from_matrix4_f16, euler_angles_xzx_from_matrix4_f32, euler_angles_xzx_from_matrix4_f64}
euler_angles_xyx_from_matrix4 :: proc{euler_angles_xyx_from_matrix4_f16, euler_angles_xyx_from_matrix4_f32, euler_angles_xyx_from_matrix4_f64}
euler_angles_yxy_from_matrix4 :: proc{euler_angles_yxy_from_matrix4_f16, euler_angles_yxy_from_matrix4_f32, euler_angles_yxy_from_matrix4_f64}
euler_angles_yzy_from_matrix4 :: proc{euler_angles_yzy_from_matrix4_f16, euler_angles_yzy_from_matrix4_f32, euler_angles_yzy_from_matrix4_f64}
euler_angles_zyz_from_matrix4 :: proc{euler_angles_zyz_from_matrix4_f16, euler_angles_zyz_from_matrix4_f32, euler_angles_zyz_from_matrix4_f64}
euler_angles_zxz_from_matrix4 :: proc{euler_angles_zxz_from_matrix4_f16, euler_angles_zxz_from_matrix4_f32, euler_angles_zxz_from_matrix4_f64}
euler_angles_xzy_from_matrix4 :: proc{euler_angles_xzy_from_matrix4_f16, euler_angles_xzy_from_matrix4_f32, euler_angles_xzy_from_matrix4_f64}
euler_angles_yzx_from_matrix4 :: proc{euler_angles_yzx_from_matrix4_f16, euler_angles_yzx_from_matrix4_f32, euler_angles_yzx_from_matrix4_f64}
euler_angles_zyx_from_matrix4 :: proc{euler_angles_zyx_from_matrix4_f16, euler_angles_zyx_from_matrix4_f32, euler_angles_zyx_from_matrix4_f64}
euler_angles_zxy_from_matrix4 :: proc{euler_angles_zxy_from_matrix4_f16, euler_angles_zxy_from_matrix4_f32, euler_angles_zxy_from_matrix4_f64}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+37 -37
View File
@@ -33,110 +33,110 @@ Vector4_Components :: enum u8 {
}
scalar_f32_swizzle1 :: proc(f: f32, c0: Scalar_Components) -> f32 {
return f;
return f
}
scalar_f32_swizzle2 :: proc(f: f32, c0, c1: Scalar_Components) -> Vector2f32 {
return {f, f};
return {f, f}
}
scalar_f32_swizzle3 :: proc(f: f32, c0, c1, c2: Scalar_Components) -> Vector3f32 {
return {f, f, f};
return {f, f, f}
}
scalar_f32_swizzle4 :: proc(f: f32, c0, c1, c2, c3: Scalar_Components) -> Vector4f32 {
return {f, f, f, f};
return {f, f, f, f}
}
vector2f32_swizzle1 :: proc(v: Vector2f32, c0: Vector2_Components) -> f32 {
return v[c0];
return v[c0]
}
vector2f32_swizzle2 :: proc(v: Vector2f32, c0, c1: Vector2_Components) -> Vector2f32 {
return {v[c0], v[c1]};
return {v[c0], v[c1]}
}
vector2f32_swizzle3 :: proc(v: Vector2f32, c0, c1, c2: Vector2_Components) -> Vector3f32 {
return {v[c0], v[c1], v[c2]};
return {v[c0], v[c1], v[c2]}
}
vector2f32_swizzle4 :: proc(v: Vector2f32, c0, c1, c2, c3: Vector2_Components) -> Vector4f32 {
return {v[c0], v[c1], v[c2], v[c3]};
return {v[c0], v[c1], v[c2], v[c3]}
}
vector3f32_swizzle1 :: proc(v: Vector3f32, c0: Vector3_Components) -> f32 {
return v[c0];
return v[c0]
}
vector3f32_swizzle2 :: proc(v: Vector3f32, c0, c1: Vector3_Components) -> Vector2f32 {
return {v[c0], v[c1]};
return {v[c0], v[c1]}
}
vector3f32_swizzle3 :: proc(v: Vector3f32, c0, c1, c2: Vector3_Components) -> Vector3f32 {
return {v[c0], v[c1], v[c2]};
return {v[c0], v[c1], v[c2]}
}
vector3f32_swizzle4 :: proc(v: Vector3f32, c0, c1, c2, c3: Vector3_Components) -> Vector4f32 {
return {v[c0], v[c1], v[c2], v[c3]};
return {v[c0], v[c1], v[c2], v[c3]}
}
vector4f32_swizzle1 :: proc(v: Vector4f32, c0: Vector4_Components) -> f32 {
return v[c0];
return v[c0]
}
vector4f32_swizzle2 :: proc(v: Vector4f32, c0, c1: Vector4_Components) -> Vector2f32 {
return {v[c0], v[c1]};
return {v[c0], v[c1]}
}
vector4f32_swizzle3 :: proc(v: Vector4f32, c0, c1, c2: Vector4_Components) -> Vector3f32 {
return {v[c0], v[c1], v[c2]};
return {v[c0], v[c1], v[c2]}
}
vector4f32_swizzle4 :: proc(v: Vector4f32, c0, c1, c2, c3: Vector4_Components) -> Vector4f32 {
return {v[c0], v[c1], v[c2], v[c3]};
return {v[c0], v[c1], v[c2], v[c3]}
}
scalar_f64_swizzle1 :: proc(f: f64, c0: Scalar_Components) -> f64 {
return f;
return f
}
scalar_f64_swizzle2 :: proc(f: f64, c0, c1: Scalar_Components) -> Vector2f64 {
return {f, f};
return {f, f}
}
scalar_f64_swizzle3 :: proc(f: f64, c0, c1, c2: Scalar_Components) -> Vector3f64 {
return {f, f, f};
return {f, f, f}
}
scalar_f64_swizzle4 :: proc(f: f64, c0, c1, c2, c3: Scalar_Components) -> Vector4f64 {
return {f, f, f, f};
return {f, f, f, f}
}
vector2f64_swizzle1 :: proc(v: Vector2f64, c0: Vector2_Components) -> f64 {
return v[c0];
return v[c0]
}
vector2f64_swizzle2 :: proc(v: Vector2f64, c0, c1: Vector2_Components) -> Vector2f64 {
return {v[c0], v[c1]};
return {v[c0], v[c1]}
}
vector2f64_swizzle3 :: proc(v: Vector2f64, c0, c1, c2: Vector2_Components) -> Vector3f64 {
return {v[c0], v[c1], v[c2]};
return {v[c0], v[c1], v[c2]}
}
vector2f64_swizzle4 :: proc(v: Vector2f64, c0, c1, c2, c3: Vector2_Components) -> Vector4f64 {
return {v[c0], v[c1], v[c2], v[c3]};
return {v[c0], v[c1], v[c2], v[c3]}
}
vector3f64_swizzle1 :: proc(v: Vector3f64, c0: Vector3_Components) -> f64 {
return v[c0];
return v[c0]
}
vector3f64_swizzle2 :: proc(v: Vector3f64, c0, c1: Vector3_Components) -> Vector2f64 {
return {v[c0], v[c1]};
return {v[c0], v[c1]}
}
vector3f64_swizzle3 :: proc(v: Vector3f64, c0, c1, c2: Vector3_Components) -> Vector3f64 {
return {v[c0], v[c1], v[c2]};
return {v[c0], v[c1], v[c2]}
}
vector3f64_swizzle4 :: proc(v: Vector3f64, c0, c1, c2, c3: Vector3_Components) -> Vector4f64 {
return {v[c0], v[c1], v[c2], v[c3]};
return {v[c0], v[c1], v[c2], v[c3]}
}
vector4f64_swizzle1 :: proc(v: Vector4f64, c0: Vector4_Components) -> f64 {
return v[c0];
return v[c0]
}
vector4f64_swizzle2 :: proc(v: Vector4f64, c0, c1: Vector4_Components) -> Vector2f64 {
return {v[c0], v[c1]};
return {v[c0], v[c1]}
}
vector4f64_swizzle3 :: proc(v: Vector4f64, c0, c1, c2: Vector4_Components) -> Vector3f64 {
return {v[c0], v[c1], v[c2]};
return {v[c0], v[c1], v[c2]}
}
vector4f64_swizzle4 :: proc(v: Vector4f64, c0, c1, c2, c3: Vector4_Components) -> Vector4f64 {
return {v[c0], v[c1], v[c2], v[c3]};
return {v[c0], v[c1], v[c2], v[c3]}
}
@@ -151,7 +151,7 @@ scalar_swizzle :: proc{
scalar_f64_swizzle2,
scalar_f64_swizzle3,
scalar_f64_swizzle4,
};
}
vector2_swizzle :: proc{
vector2f32_swizzle1,
@@ -162,7 +162,7 @@ vector2_swizzle :: proc{
vector2f64_swizzle2,
vector2f64_swizzle3,
vector2f64_swizzle4,
};
}
vector3_swizzle :: proc{
vector3f32_swizzle1,
@@ -173,7 +173,7 @@ vector3_swizzle :: proc{
vector3f64_swizzle2,
vector3f64_swizzle3,
vector3f64_swizzle4,
};
}
vector4_swizzle :: proc{
vector4f32_swizzle1,
@@ -184,7 +184,7 @@ vector4_swizzle :: proc{
vector4f64_swizzle2,
vector4f64_swizzle3,
vector4f64_swizzle4,
};
}
swizzle :: proc{
scalar_f32_swizzle1,
@@ -219,4 +219,4 @@ swizzle :: proc{
vector4f64_swizzle2,
vector4f64_swizzle3,
vector4f64_swizzle4,
};
}
+635 -635
View File
File diff suppressed because it is too large Load Diff
+16 -16
View File
@@ -18,7 +18,7 @@ import "core:math"
// sample = norm_float64() * std_dev + mean
//
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
rn :: 3.442619855899;
rn :: 3.442619855899
@(static)
kn := [128]u32{
@@ -48,7 +48,7 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
0x7e3b737a, 0x7e268c2f, 0x7e0e3ff5, 0x7df1aa5d, 0x7dcf8c72,
0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,
0x7ba90bdc, 0x7a722176, 0x77d664e5,
};
}
@(static)
wn := [128]f32{
@@ -84,7 +84,7 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
1.1781276e-09, 1.1962995e-09, 1.2158287e-09, 1.2369856e-09,
1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,
1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,
};
}
@(static)
fn := [128]f32{
@@ -114,39 +114,39 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
0.044660863, 0.040742867, 0.03688439, 0.033087887, 0.029356318,
0.025693292, 0.022103304, 0.018592102, 0.015167298, 0.011839478,
0.008624485, 0.005548995, 0.0026696292,
};
}
r := r;
r := r
if r == nil {
// NOTE(bill, 2020-09-07): Do this so that people can
// enforce the global random state if necessary with `nil`
r = &global_rand;
r = &global_rand
}
for {
j := i32(uint32(r));
i := j & 0x7f;
x := f64(j) * f64(wn[i]);
j := i32(uint32(r))
i := j & 0x7f
x := f64(j) * f64(wn[i])
if u32(abs(j)) < kn[i] {
// 99% of the time this will be hit
return x;
return x
}
if i == 0 {
for {
x = -math.ln(float64(r)) * (1.0/ rn);
y := -math.ln(float64(r));
x = -math.ln(float64(r)) * (1.0/ rn)
y := -math.ln(float64(r))
if y+y >= x*x {
break;
break
}
}
return j > 0 ? rn + x : -rn - x;
return j > 0 ? rn + x : -rn - x
}
if fn[i]+f32(float64(r))*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
return x;
return x
}
}
return 0; // NOTE(bill): Will never be hit but this is here for sanity's sake
return 0 // NOTE(bill): Will never be hit but this is here for sanity's sake
}
+72 -72
View File
@@ -7,160 +7,160 @@ Rand :: struct {
@(private)
_GLOBAL_SEED_DATA := 1234567890;
_GLOBAL_SEED_DATA := 1234567890
@(private)
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)));
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)))
set_global_seed :: proc(seed: u64) {
init(&global_rand, seed);
init(&global_rand, seed)
}
create :: proc(seed: u64) -> Rand {
r: Rand;
init(&r, seed);
return r;
r: Rand
init(&r, seed)
return r
}
init :: proc(r: ^Rand, seed: u64) {
r.state = 0;
r.inc = (seed << 1) | 1;
_random(r);
r.state += seed;
_random(r);
r.state = 0
r.inc = (seed << 1) | 1
_random(r)
r.state += seed
_random(r)
}
_random :: proc(r: ^Rand) -> u32 {
r := r;
r := r
if r == nil {
// NOTE(bill, 2020-09-07): Do this so that people can
// enforce the global random state if necessary with `nil`
r = &global_rand;
r = &global_rand
}
old_state := r.state;
r.state = old_state * 6364136223846793005 + (r.inc|1);
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27);
rot := u32(old_state >> 59);
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31));
old_state := r.state
r.state = old_state * 6364136223846793005 + (r.inc|1)
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27)
rot := u32(old_state >> 59)
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31))
}
uint32 :: proc(r: ^Rand = nil) -> u32 { return _random(r); }
uint32 :: proc(r: ^Rand = nil) -> u32 { return _random(r) }
uint64 :: proc(r: ^Rand = nil) -> u64 {
a := u64(_random(r));
b := u64(_random(r));
return (a<<32) | b;
a := u64(_random(r))
b := u64(_random(r))
return (a<<32) | b
}
uint128 :: proc(r: ^Rand = nil) -> u128 {
a := u128(_random(r));
b := u128(_random(r));
c := u128(_random(r));
d := u128(_random(r));
return (a<<96) | (b<<64) | (c<<32) | d;
a := u128(_random(r))
b := u128(_random(r))
c := u128(_random(r))
d := u128(_random(r))
return (a<<96) | (b<<64) | (c<<32) | d
}
int31 :: proc(r: ^Rand = nil) -> i32 { return i32(uint32(r) << 1 >> 1); }
int63 :: proc(r: ^Rand = nil) -> i64 { return i64(uint64(r) << 1 >> 1); }
int127 :: proc(r: ^Rand = nil) -> i128 { return i128(uint128(r) << 1 >> 1); }
int31 :: proc(r: ^Rand = nil) -> i32 { return i32(uint32(r) << 1 >> 1) }
int63 :: proc(r: ^Rand = nil) -> i64 { return i64(uint64(r) << 1 >> 1) }
int127 :: proc(r: ^Rand = nil) -> i128 { return i128(uint128(r) << 1 >> 1) }
int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
if n <= 0 {
panic("Invalid argument to int31_max");
panic("Invalid argument to int31_max")
}
if n&(n-1) == 0 {
return int31(r) & (n-1);
return int31(r) & (n-1)
}
max := i32((1<<31) - 1 - (1<<31)&u32(n));
v := int31(r);
max := i32((1<<31) - 1 - (1<<31)&u32(n))
v := int31(r)
for v > max {
v = int31(r);
v = int31(r)
}
return v % n;
return v % n
}
int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
if n <= 0 {
panic("Invalid argument to int63_max");
panic("Invalid argument to int63_max")
}
if n&(n-1) == 0 {
return int63(r) & (n-1);
return int63(r) & (n-1)
}
max := i64((1<<63) - 1 - (1<<63)&u64(n));
v := int63(r);
max := i64((1<<63) - 1 - (1<<63)&u64(n))
v := int63(r)
for v > max {
v = int63(r);
v = int63(r)
}
return v % n;
return v % n
}
int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
if n <= 0 {
panic("Invalid argument to int127_max");
panic("Invalid argument to int127_max")
}
if n&(n-1) == 0 {
return int127(r) & (n-1);
return int127(r) & (n-1)
}
max := i128((1<<63) - 1 - (1<<63)&u128(n));
v := int127(r);
max := i128((1<<63) - 1 - (1<<63)&u128(n))
v := int127(r)
for v > max {
v = int127(r);
v = int127(r)
}
return v % n;
return v % n
}
int_max :: proc(n: int, r: ^Rand = nil) -> int {
if n <= 0 {
panic("Invalid argument to int_max");
panic("Invalid argument to int_max")
}
when size_of(int) == 4 {
return int(int31_max(i32(n), r));
return int(int31_max(i32(n), r))
} else {
return int(int63_max(i64(n), r));
return int(int63_max(i64(n), r))
}
}
float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53); }
float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)); }
float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53) }
float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)) }
float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo; }
float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo; }
float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo }
float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo }
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
pos := i8(0);
val := i64(0);
pos := i8(0)
val := i64(0)
for n = 0; n < len(p); n += 1 {
if pos == 0 {
val = int63(r);
pos = 7;
val = int63(r)
pos = 7
}
p[n] = byte(val);
val >>= 8;
pos -= 1;
p[n] = byte(val)
val >>= 8
pos -= 1
}
return;
return
}
// perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n)
perm :: proc(n: int, r: ^Rand = nil) -> []int {
m := make([]int, n);
m := make([]int, n)
for i := 0; i < n; i += 1 {
j := int_max(i+1, r);
m[i] = m[j];
m[j] = i;
j := int_max(i+1, r)
m[i] = m[j]
m[j] = i
}
return m;
return m
}
shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
n := i64(len(array));
n := i64(len(array))
if n < 2 {
return;
return
}
for i := i64(0); i < n; i += 1 {
j := int63_max(n, r);
array[i], array[j] = array[j], array[i];
j := int63_max(n, r)
array[i], array[j] = array[j], array[i]
}
}
+120 -120
View File
@@ -3,7 +3,7 @@ package mem
import "core:runtime"
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
Allocator_Mode :: runtime.Allocator_Mode;
Allocator_Mode :: runtime.Allocator_Mode
/*
Allocator_Mode :: enum byte {
Alloc,
@@ -14,12 +14,12 @@ Allocator_Mode :: enum byte {
}
*/
Allocator_Mode_Set :: runtime.Allocator_Mode_Set;
Allocator_Mode_Set :: runtime.Allocator_Mode_Set
/*
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode];
*/
Allocator_Query_Info :: runtime.Allocator_Query_Info;
Allocator_Query_Info :: runtime.Allocator_Query_Info
/*
Allocator_Query_Info :: struct {
pointer: rawptr,
@@ -28,7 +28,7 @@ Allocator_Query_Info :: struct {
}
*/
Allocator_Error :: runtime.Allocator_Error;
Allocator_Error :: runtime.Allocator_Error
/*
Allocator_Error :: enum byte {
None = 0,
@@ -38,14 +38,14 @@ Allocator_Error :: enum byte {
Mode_Not_Implemented = 4,
}
*/
Allocator_Proc :: runtime.Allocator_Proc;
Allocator_Proc :: runtime.Allocator_Proc
/*
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error);
*/
Allocator :: runtime.Allocator;
Allocator :: runtime.Allocator
/*
Allocator :: struct {
procedure: Allocator_Proc,
@@ -53,148 +53,148 @@ Allocator :: struct {
}
*/
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if size == 0 {
return nil;
return nil
}
if allocator.procedure == nil {
return nil;
return nil
}
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
_ = err;
return raw_data(data);
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
_ = err
return raw_data(data)
}
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
if size == 0 {
return nil, nil;
return nil, nil
}
if allocator.procedure == nil {
return nil, nil;
return nil, nil
}
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
}
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil {
return nil;
return nil
}
if allocator.procedure == nil {
return nil;
return nil
}
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc);
return err;
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc)
return err
}
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if bytes == nil {
return nil;
return nil
}
if allocator.procedure == nil {
return nil;
return nil
}
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc);
return err;
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc)
return err
}
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if allocator.procedure != nil {
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc);
return err;
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc)
return err
}
return nil;
return nil
}
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if allocator.procedure == nil {
return nil;
return nil
}
if new_size == 0 {
if ptr != nil {
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
}
return nil;
return nil
} else if ptr == nil {
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
_ = err;
return nil;
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
_ = err
return nil
}
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
if err != nil {
return nil;
return nil
}
runtime.copy(data, byte_slice(ptr, old_size));
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
return raw_data(data);
runtime.copy(data, byte_slice(ptr, old_size))
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
return raw_data(data)
}
return raw_data(data);
return raw_data(data)
}
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
if allocator.procedure == nil {
return nil, nil;
return nil, nil
}
ptr := raw_data(old_data);
old_size := len(old_data);
ptr := raw_data(old_data)
old_size := len(old_data)
if new_size == 0 {
if ptr != nil {
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
return nil, err;
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
return nil, err
}
return nil, nil;
return nil, nil
} else if ptr == nil {
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
}
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
if err != nil {
return data, err;
return data, err
}
runtime.copy(data, old_data);
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
runtime.copy(data, old_data)
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
}
return data, err;
return data, err
}
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
if allocator.procedure != nil {
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc);
return set;
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc)
return set
}
return nil;
return nil
}
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
props.pointer = pointer;
props.pointer = pointer
if allocator.procedure != nil {
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc);
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc)
}
return;
return
}
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
free(raw_data(str), allocator, loc);
free(raw_data(str), allocator, loc)
}
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
free((^byte)(str), allocator, loc);
free((^byte)(str), allocator, loc)
}
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
free(raw_data(array), array.allocator, loc);
free(raw_data(array), array.allocator, loc)
}
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
free(raw_data(array), allocator, loc);
free(raw_data(array), allocator, loc)
}
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
raw := transmute(Raw_Map)m;
delete_slice(raw.hashes, raw.entries.allocator, loc);
free(raw.entries.data, raw.entries.allocator, loc);
raw := transmute(Raw_Map)m
delete_slice(raw.hashes, raw.entries.allocator, loc)
free(raw.entries.data, raw.entries.allocator, loc)
}
@@ -204,72 +204,72 @@ delete :: proc{
delete_dynamic_array,
delete_slice,
delete_map,
};
}
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) {
return new_aligned(T, align_of(T), allocator, loc);
return new_aligned(T, align_of(T), allocator, loc)
}
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
data := alloc_bytes(size_of(T), alignment, allocator, loc) or_return;
t = (^T)(raw_data(data));
return;
data := alloc_bytes(size_of(T), alignment, allocator, loc) or_return
t = (^T)(raw_data(data))
return
}
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
data := alloc_bytes(size_of(T), alignment, allocator, loc) or_return;
t = (^T)(raw_data(data));
data := alloc_bytes(size_of(T), alignment, allocator, loc) or_return
t = (^T)(raw_data(data))
if t != nil {
t^ = data;
t^ = data
}
return;
return
}
DEFAULT_RESERVE_CAPACITY :: 16;
DEFAULT_RESERVE_CAPACITY :: 16
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
runtime.make_slice_error_loc(loc, len);
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return;
runtime.make_slice_error_loc(loc, len)
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
if data == nil && size_of(E) != 0 {
return;
return
}
slice = transmute(T)Raw_Slice{raw_data(data), len};
return;
slice = transmute(T)Raw_Slice{raw_data(data), len}
return
}
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_aligned(T, len, align_of(E), allocator, loc);
return make_aligned(T, len, align_of(E), allocator, loc)
}
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc);
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
}
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
}
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) {
runtime.make_dynamic_array_error_loc(loc, len, cap);
data := alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return;
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator};
runtime.make_dynamic_array_error_loc(loc, len, cap)
data := alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
if data == nil && size_of(E) != 0 {
s.len, s.cap = 0, 0;
s.len, s.cap = 0, 0
}
array = transmute(T)s;
return;
array = transmute(T)s
return
}
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
runtime.make_map_expr_error_loc(loc, cap);
context.allocator = allocator;
runtime.make_map_expr_error_loc(loc, cap)
context.allocator = allocator
m: T;
reserve_map(&m, cap);
return m;
m: T
reserve_map(&m, cap)
return m
}
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
runtime.make_slice_error_loc(loc, len);
data := alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return;
runtime.make_slice_error_loc(loc, len)
data := alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return
if data == nil && size_of(E) != 0 {
return;
return
}
mp = cast(T)raw_data(data);
return;
mp = cast(T)raw_data(data)
return
}
make :: proc{
@@ -279,55 +279,55 @@ make :: proc{
make_dynamic_array_len_cap,
make_map,
make_multi_pointer,
};
}
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> rawptr {
if old_memory == nil {
return alloc(new_size, alignment, allocator, loc);
return alloc(new_size, alignment, allocator, loc)
}
if new_size == 0 {
free(old_memory, allocator, loc);
return nil;
free(old_memory, allocator, loc)
return nil
}
if new_size == old_size {
return old_memory;
return old_memory
}
new_memory := alloc(new_size, alignment, allocator, loc);
new_memory := alloc(new_size, alignment, allocator, loc)
if new_memory == nil {
return nil;
return nil
}
copy(new_memory, old_memory, min(old_size, new_size));
free(old_memory, allocator, loc);
return new_memory;
copy(new_memory, old_memory, min(old_size, new_size))
free(old_memory, allocator, loc)
return new_memory
}
default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
old_memory := raw_data(old_data);
old_size := len(old_data);
old_memory := raw_data(old_data)
old_size := len(old_data)
if old_memory == nil {
return alloc_bytes(new_size, alignment, allocator, loc);
return alloc_bytes(new_size, alignment, allocator, loc)
}
if new_size == 0 {
err := free_bytes(old_data, allocator, loc);
return nil, err;
err := free_bytes(old_data, allocator, loc)
return nil, err
}
if new_size == old_size {
return old_data, .None;
return old_data, .None
}
new_memory, err := alloc_bytes(new_size, alignment, allocator, loc);
new_memory, err := alloc_bytes(new_size, alignment, allocator, loc)
if new_memory == nil || err != nil {
return nil, err;
return nil, err
}
runtime.copy(new_memory, old_data);
free_bytes(old_data, allocator, loc);
return new_memory, err;
runtime.copy(new_memory, old_data)
free_bytes(old_data, allocator, loc)
return new_memory, err
}
+317 -317
View File
File diff suppressed because it is too large Load Diff
+105 -105
View File
@@ -4,66 +4,66 @@ import "core:runtime"
import "core:intrinsics"
set :: proc(data: rawptr, value: byte, len: int) -> rawptr {
return runtime.memset(data, i32(value), len);
return runtime.memset(data, i32(value), len)
}
zero :: proc(data: rawptr, len: int) -> rawptr {
return set(data, 0, len);
return set(data, 0, len)
}
zero_item :: proc(item: $P/^$T) {
set(item, 0, size_of(T));
set(item, 0, size_of(T))
}
zero_slice :: proc(data: $T/[]$E) {
zero(raw_data(data), size_of(E)*len(data));
zero(raw_data(data), size_of(E)*len(data))
}
copy :: proc(dst, src: rawptr, len: int) -> rawptr {
return runtime.mem_copy(dst, src, len);
return runtime.mem_copy(dst, src, len)
}
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
return runtime.mem_copy_non_overlapping(dst, src, len);
return runtime.mem_copy_non_overlapping(dst, src, len)
}
compare :: proc(a, b: []byte) -> int {
res := compare_byte_ptrs(raw_data(a), raw_data(b), min(len(a), len(b)));
res := compare_byte_ptrs(raw_data(a), raw_data(b), min(len(a), len(b)))
if res == 0 && len(a) != len(b) {
return len(a) <= len(b) ? -1 : +1;
return len(a) <= len(b) ? -1 : +1
} else if len(a) == 0 && len(b) == 0 {
return 0;
return 0
}
return res;
return res
}
compare_byte_ptrs :: proc(a, b: ^byte, n: int) -> int #no_bounds_check {
switch {
case a == b:
return 0;
return 0
case a == nil:
return -1;
return -1
case b == nil:
return -1;
return -1
case n == 0:
return 0;
return 0
}
x := slice_ptr(a, n);
y := slice_ptr(b, n);
x := slice_ptr(a, n)
y := slice_ptr(b, n)
SU :: size_of(uintptr);
fast := n/SU + 1;
offset := (fast-1)*SU;
curr_block := 0;
SU :: size_of(uintptr)
fast := n/SU + 1
offset := (fast-1)*SU
curr_block := 0
if n < SU {
fast = 0;
fast = 0
}
la := slice_ptr((^uintptr)(a), fast);
lb := slice_ptr((^uintptr)(b), fast);
la := slice_ptr((^uintptr)(a), fast)
lb := slice_ptr((^uintptr)(b), fast)
for /**/; curr_block < fast; curr_block += 1 {
if la[curr_block] ~ lb[curr_block] != 0 {
for pos := curr_block*SU; pos < n; pos += 1 {
if x[pos] ~ y[pos] != 0 {
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1;
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1
}
}
}
@@ -71,96 +71,96 @@ compare_byte_ptrs :: proc(a, b: ^byte, n: int) -> int #no_bounds_check {
for /**/; offset < n; offset += 1 {
if x[offset] ~ y[offset] != 0 {
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1;
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1
}
}
return 0;
return 0
}
check_zero :: proc(data: []byte) -> bool {
return check_zero_ptr(raw_data(data), len(data));
return check_zero_ptr(raw_data(data), len(data))
}
check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
switch {
case len <= 0:
return true;
return true
case ptr == nil:
return true;
return true
}
start := uintptr(ptr);
start_aligned := align_forward_uintptr(start, align_of(uintptr));
end := start + uintptr(len);
end_aligned := align_backward_uintptr(end, align_of(uintptr));
start := uintptr(ptr)
start_aligned := align_forward_uintptr(start, align_of(uintptr))
end := start + uintptr(len)
end_aligned := align_backward_uintptr(end, align_of(uintptr))
for b in start..<start_aligned {
if (^byte)(b)^ != 0 {
return false;
return false
}
}
for b := start_aligned; b < end_aligned; b += size_of(uintptr) {
if (^uintptr)(b)^ != 0 {
return false;
return false
}
}
for b in end_aligned..<end {
if (^byte)(b)^ != 0 {
return false;
return false
}
}
return true;
return true
}
simple_equal :: proc(a, b: $T) -> bool where intrinsics.type_is_simple_compare(T) {
a, b := a, b;
return compare_byte_ptrs((^byte)(&a), (^byte)(&b), size_of(T)) == 0;
a, b := a, b
return compare_byte_ptrs((^byte)(&a), (^byte)(&b), size_of(T)) == 0
}
compare_ptrs :: proc(a, b: rawptr, n: int) -> int {
return compare_byte_ptrs((^byte)(a), (^byte)(b), n);
return compare_byte_ptrs((^byte)(a), (^byte)(b), n)
}
ptr_offset :: proc(ptr: $P/^$T, n: int) -> P {
new := int(uintptr(ptr)) + size_of(T)*n;
return P(uintptr(new));
new := int(uintptr(ptr)) + size_of(T)*n
return P(uintptr(new))
}
ptr_sub :: proc(a, b: $P/^$T) -> int {
return (int(uintptr(a)) - int(uintptr(b)))/size_of(T);
return (int(uintptr(a)) - int(uintptr(b)))/size_of(T)
}
slice_ptr :: proc(ptr: ^$T, len: int) -> []T {
return ([^]T)(ptr)[:len];
return ([^]T)(ptr)[:len]
}
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
return ([^]u8)(data)[:max(len, 0)];
return ([^]u8)(data)[:max(len, 0)]
}
slice_to_bytes :: proc(slice: $E/[]$T) -> []byte {
s := transmute(Raw_Slice)slice;
s.len *= size_of(T);
return transmute([]byte)s;
s := transmute(Raw_Slice)slice
s.len *= size_of(T)
return transmute([]byte)s
}
slice_data_cast :: proc($T: typeid/[]$A, slice: $S/[]$B) -> T {
when size_of(A) == 0 || size_of(B) == 0 {
return nil;
return nil
} else {
s := transmute(Raw_Slice)slice;
s.len = (len(slice) * size_of(B)) / size_of(A);
return transmute(T)s;
s := transmute(Raw_Slice)slice
s.len = (len(slice) * size_of(B)) / size_of(A)
return transmute(T)s
}
}
slice_to_components :: proc(slice: $E/[]$T) -> (data: ^T, len: int) {
s := transmute(Raw_Slice)slice;
return s.data, s.len;
s := transmute(Raw_Slice)slice
return s.data, s.len
}
buffer_from_slice :: proc(backing: $T/[]$E) -> [dynamic]E {
@@ -169,128 +169,128 @@ buffer_from_slice :: proc(backing: $T/[]$E) -> [dynamic]E {
len = 0,
cap = len(backing),
allocator = nil_allocator(),
};
}
}
ptr_to_bytes :: proc(ptr: ^$T, len := 1) -> []byte {
assert(len >= 0);
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)};
assert(len >= 0)
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)}
}
any_to_bytes :: proc(val: any) -> []byte {
ti := type_info_of(val.id);
size := ti != nil ? ti.size : 0;
return transmute([]byte)Raw_Slice{val.data, size};
ti := type_info_of(val.id)
size := ti != nil ? ti.size : 0
return transmute([]byte)Raw_Slice{val.data, size}
}
kilobytes :: proc(x: int) -> int { return (x) * 1024; }
megabytes :: proc(x: int) -> int { return kilobytes(x) * 1024; }
gigabytes :: proc(x: int) -> int { return megabytes(x) * 1024; }
terabytes :: proc(x: int) -> int { return gigabytes(x) * 1024; }
kilobytes :: proc(x: int) -> int { return (x) * 1024 }
megabytes :: proc(x: int) -> int { return kilobytes(x) * 1024 }
gigabytes :: proc(x: int) -> int { return megabytes(x) * 1024 }
terabytes :: proc(x: int) -> int { return gigabytes(x) * 1024 }
is_power_of_two :: proc(x: uintptr) -> bool {
if x <= 0 {
return false;
return false
}
return (x & (x-1)) == 0;
return (x & (x-1)) == 0
}
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
return rawptr(align_forward_uintptr(uintptr(ptr), align));
return rawptr(align_forward_uintptr(uintptr(ptr), align))
}
align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
assert(is_power_of_two(align));
assert(is_power_of_two(align))
p := ptr;
modulo := p & (align-1);
p := ptr
modulo := p & (align-1)
if modulo != 0 {
p += align - modulo;
p += align - modulo
}
return p;
return p
}
align_forward_int :: proc(ptr, align: int) -> int {
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)));
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)))
}
align_forward_uint :: proc(ptr, align: uint) -> uint {
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)));
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)))
}
align_backward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
return rawptr(align_backward_uintptr(uintptr(ptr), align));
return rawptr(align_backward_uintptr(uintptr(ptr), align))
}
align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
assert(is_power_of_two(align));
return align_forward_uintptr(ptr - align + 1, align);
assert(is_power_of_two(align))
return align_forward_uintptr(ptr - align + 1, align)
}
align_backward_int :: proc(ptr, align: int) -> int {
return int(align_backward_uintptr(uintptr(ptr), uintptr(align)));
return int(align_backward_uintptr(uintptr(ptr), uintptr(align)))
}
align_backward_uint :: proc(ptr, align: uint) -> uint {
return uint(align_backward_uintptr(uintptr(ptr), uintptr(align)));
return uint(align_backward_uintptr(uintptr(ptr), uintptr(align)))
}
context_from_allocator :: proc(a: Allocator) -> type_of(context) {
context.allocator = a;
return context;
context.allocator = a
return context
}
reinterpret_copy :: proc($T: typeid, ptr: rawptr) -> (value: T) {
copy(&value, ptr, size_of(T));
return;
copy(&value, ptr, size_of(T))
return
}
Fixed_Byte_Buffer :: distinct [dynamic]byte;
Fixed_Byte_Buffer :: distinct [dynamic]byte
make_fixed_byte_buffer :: proc(backing: []byte) -> Fixed_Byte_Buffer {
s := transmute(Raw_Slice)backing;
d: Raw_Dynamic_Array;
d.data = s.data;
d.len = 0;
d.cap = s.len;
d.allocator = nil_allocator();
return transmute(Fixed_Byte_Buffer)d;
s := transmute(Raw_Slice)backing
d: Raw_Dynamic_Array
d.data = s.data
d.len = 0
d.cap = s.len
d.allocator = nil_allocator()
return transmute(Fixed_Byte_Buffer)d
}
align_formula :: proc(size, align: int) -> int {
result := size + align-1;
return result - result%align;
result := size + align-1
return result - result%align
}
calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int) -> int {
p, a := ptr, align;
modulo := p & (a-1);
p, a := ptr, align
modulo := p & (a-1)
padding := uintptr(0);
padding := uintptr(0)
if modulo != 0 {
padding = a - modulo;
padding = a - modulo
}
needed_space := uintptr(header_size);
needed_space := uintptr(header_size)
if padding < needed_space {
needed_space -= padding;
needed_space -= padding
if needed_space & (a-1) > 0 {
padding += align * (1+(needed_space/align));
padding += align * (1+(needed_space/align))
} else {
padding += align * (needed_space/align);
padding += align * (needed_space/align)
}
}
return int(padding);
return int(padding)
}
clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> (new_slice: T) {
new_slice, _ = make(T, len(slice), allocator, loc);
runtime.copy(new_slice, slice);
return new_slice;
new_slice, _ = make(T, len(slice), allocator, loc)
runtime.copy(new_slice, slice)
return new_slice
}
+12 -12
View File
@@ -31,31 +31,31 @@ Raw_Map :: struct {
entries: Raw_Dynamic_Array,
}
Raw_Complex64 :: struct {real, imag: f32};
Raw_Complex128 :: struct {real, imag: f64};
Raw_Quaternion128 :: struct {imag, jmag, kmag: f32, real: f32};
Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64};
Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32};
Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64};
Raw_Complex64 :: struct {real, imag: f32}
Raw_Complex128 :: struct {real, imag: f64}
Raw_Quaternion128 :: struct {imag, jmag, kmag: f32, real: f32}
Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64}
Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32}
Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64}
make_any :: proc(data: rawptr, id: typeid) -> any {
return transmute(any)Raw_Any{data, id};
return transmute(any)Raw_Any{data, id}
}
raw_array_data :: proc(a: $P/^($T/[$N]$E)) -> ^E {
return (^E)(a);
return (^E)(a)
}
raw_string_data :: proc(s: $T/string) -> ^byte {
return (transmute(Raw_String)s).data;
return (transmute(Raw_String)s).data
}
raw_slice_data :: proc(a: $T/[]$E) -> ^E {
return cast(^E)(transmute(Raw_Slice)a).data;
return cast(^E)(transmute(Raw_Slice)a).data
}
raw_dynamic_array_data :: proc(a: $T/[dynamic]$E) -> ^E {
return cast(^E)(transmute(Raw_Dynamic_Array)a).data;
return cast(^E)(transmute(Raw_Dynamic_Array)a).data
}
raw_data :: proc{raw_array_data, raw_string_data, raw_slice_data, raw_dynamic_array_data};
raw_data :: proc{raw_array_data, raw_string_data, raw_slice_data, raw_dynamic_array_data}
Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
+14 -14
View File
@@ -8,7 +8,7 @@ Proc_Tag :: enum {
Optional_Ok,
Optional_Second,
}
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
Proc_Tags :: distinct bit_set[Proc_Tag; u32]
Proc_Inlining :: enum u32 {
None = 0,
@@ -28,7 +28,7 @@ Node_State_Flag :: enum {
Bounds_Check,
No_Bounds_Check,
}
Node_State_Flags :: distinct bit_set[Node_State_Flag];
Node_State_Flags :: distinct bit_set[Node_State_Flag]
Node :: struct {
pos: tokenizer.Pos,
@@ -526,18 +526,18 @@ Foreign_Import_Decl :: struct {
// Other things
unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
val = expr;
val = expr
if expr == nil {
return;
return
}
for {
e, ok := val.derived.(Paren_Expr);
e, ok := val.derived.(Paren_Expr)
if !ok || e.expr == nil {
break;
break
}
val = e.expr;
val = e.expr
}
return;
return
}
Field_Flag :: enum {
@@ -554,16 +554,16 @@ Field_Flag :: enum {
Typeid_Token,
}
Field_Flags :: distinct bit_set[Field_Flag];
Field_Flags :: distinct bit_set[Field_Flag]
Field_Flags_Struct :: Field_Flags{
.Using,
.Tags,
};
}
Field_Flags_Record_Poly_Params :: Field_Flags{
.Typeid_Token,
.Default_Parameters,
};
}
Field_Flags_Signature :: Field_Flags{
.Ellipsis,
.Using,
@@ -571,10 +571,10 @@ Field_Flags_Signature :: Field_Flags{
.C_Vararg,
.Auto_Cast,
.Default_Parameters,
};
}
Field_Flags_Signature_Params :: Field_Flags_Signature | {Field_Flag.Typeid_Token};
Field_Flags_Signature_Results :: Field_Flags_Signature;
Field_Flags_Signature_Params :: Field_Flags_Signature | {Field_Flag.Typeid_Token}
Field_Flags_Signature_Results :: Field_Flags_Signature
Proc_Group :: struct {
+152 -152
View File
@@ -5,13 +5,13 @@ import "core:fmt"
import "core:odin/tokenizer"
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
n, _ := mem.new(T);
n.pos = pos;
n.end = end;
n.derived = n^;
base: ^Node = n; // dummy check
_ = base; // "Use" type to make -vet happy
return n;
n, _ := mem.new(T)
n.pos = pos
n.end = end
n.derived = n^
base: ^Node = n // dummy check
_ = base // "Use" type to make -vet happy
return n
}
clone :: proc{
@@ -21,65 +21,65 @@ clone :: proc{
clone_decl,
clone_array,
clone_dynamic_array,
};
}
clone_array :: proc(array: $A/[]^$T) -> A {
if len(array) == 0 {
return nil;
return nil
}
res := make(A, len(array));
res := make(A, len(array))
for elem, i in array {
res[i] = auto_cast clone(elem);
res[i] = auto_cast clone(elem)
}
return res;
return res
}
clone_dynamic_array :: proc(array: $A/[dynamic]^$T) -> A {
if len(array) == 0 {
return nil;
return nil
}
res := make(A, len(array));
res := make(A, len(array))
for elem, i in array {
res[i] = auto_cast clone(elem);
res[i] = auto_cast clone(elem)
}
return res;
return res
}
clone_expr :: proc(node: ^Expr) -> ^Expr {
return cast(^Expr)clone_node(node);
return cast(^Expr)clone_node(node)
}
clone_stmt :: proc(node: ^Stmt) -> ^Stmt {
return cast(^Stmt)clone_node(node);
return cast(^Stmt)clone_node(node)
}
clone_decl :: proc(node: ^Decl) -> ^Decl {
return cast(^Decl)clone_node(node);
return cast(^Decl)clone_node(node)
}
clone_node :: proc(node: ^Node) -> ^Node {
if node == nil {
return nil;
return nil
}
size := size_of(Node);
align := align_of(Node);
ti := type_info_of(node.derived.id);
size := size_of(Node)
align := align_of(Node)
ti := type_info_of(node.derived.id)
if ti != nil {
size = ti.size;
align = ti.align;
size = ti.size
align = ti.align
}
switch in node.derived {
case Package, File:
panic("Cannot clone this node type");
panic("Cannot clone this node type")
}
res := cast(^Node)mem.alloc(size, align);
src: rawptr = node;
res := cast(^Node)mem.alloc(size, align)
src: rawptr = node
if node.derived != nil {
src = node.derived.data;
src = node.derived.data
}
mem.copy(res, src, size);
res.derived.data = rawptr(res);
res.derived.id = node.derived.id;
mem.copy(res, src, size)
res.derived.data = rawptr(res)
res.derived.id = node.derived.id
switch r in &res.derived {
case Bad_Expr:
@@ -89,196 +89,196 @@ clone_node :: proc(node: ^Node) -> ^Node {
case Basic_Lit:
case Ellipsis:
r.expr = clone(r.expr);
r.expr = clone(r.expr)
case Proc_Lit:
r.type = auto_cast clone(r.type);
r.body = clone(r.body);
r.type = auto_cast clone(r.type)
r.body = clone(r.body)
case Comp_Lit:
r.type = clone(r.type);
r.elems = clone(r.elems);
r.type = clone(r.type)
r.elems = clone(r.elems)
case Tag_Expr:
r.expr = clone(r.expr);
r.expr = clone(r.expr)
case Unary_Expr:
r.expr = clone(r.expr);
r.expr = clone(r.expr)
case Binary_Expr:
r.left = clone(r.left);
r.right = clone(r.right);
r.left = clone(r.left)
r.right = clone(r.right)
case Paren_Expr:
r.expr = clone(r.expr);
r.expr = clone(r.expr)
case Selector_Expr:
r.expr = clone(r.expr);
r.field = auto_cast clone(r.field);
r.expr = clone(r.expr)
r.field = auto_cast clone(r.field)
case Implicit_Selector_Expr:
r.field = auto_cast clone(r.field);
r.field = auto_cast clone(r.field)
case Selector_Call_Expr:
r.expr = clone(r.expr);
r.call = auto_cast clone(r.call);
r.expr = clone(r.expr)
r.call = auto_cast clone(r.call)
case Index_Expr:
r.expr = clone(r.expr);
r.index = clone(r.index);
r.expr = clone(r.expr)
r.index = clone(r.index)
case Deref_Expr:
r.expr = clone(r.expr);
r.expr = clone(r.expr)
case Slice_Expr:
r.expr = clone(r.expr);
r.low = clone(r.low);
r.high = clone(r.high);
r.expr = clone(r.expr)
r.low = clone(r.low)
r.high = clone(r.high)
case Call_Expr:
r.expr = clone(r.expr);
r.args = clone(r.args);
r.expr = clone(r.expr)
r.args = clone(r.args)
case Field_Value:
r.field = clone(r.field);
r.value = clone(r.value);
r.field = clone(r.field)
r.value = clone(r.value)
case Ternary_If_Expr:
r.x = clone(r.x);
r.cond = clone(r.cond);
r.y = clone(r.y);
r.x = clone(r.x)
r.cond = clone(r.cond)
r.y = clone(r.y)
case Ternary_When_Expr:
r.x = clone(r.x);
r.cond = clone(r.cond);
r.y = clone(r.y);
r.x = clone(r.x)
r.cond = clone(r.cond)
r.y = clone(r.y)
case Or_Else_Expr:
r.x = clone(r.x);
r.y = clone(r.y);
r.x = clone(r.x)
r.y = clone(r.y)
case Or_Return_Expr:
r.expr = clone(r.expr);
r.expr = clone(r.expr)
case Type_Assertion:
r.expr = clone(r.expr);
r.type = clone(r.type);
r.expr = clone(r.expr)
r.type = clone(r.type)
case Type_Cast:
r.type = clone(r.type);
r.expr = clone(r.expr);
r.type = clone(r.type)
r.expr = clone(r.expr)
case Auto_Cast:
r.expr = clone(r.expr);
r.expr = clone(r.expr)
case Inline_Asm_Expr:
r.param_types = clone(r.param_types);
r.return_type = clone(r.return_type);
r.constraints_string = clone(r.constraints_string);
r.asm_string = clone(r.asm_string);
r.param_types = clone(r.param_types)
r.return_type = clone(r.return_type)
r.constraints_string = clone(r.constraints_string)
r.asm_string = clone(r.asm_string)
case Bad_Stmt:
// empty
case Empty_Stmt:
// empty
case Expr_Stmt:
r.expr = clone(r.expr);
r.expr = clone(r.expr)
case Tag_Stmt:
r.stmt = clone(r.stmt);
r.stmt = clone(r.stmt)
case Assign_Stmt:
r.lhs = clone(r.lhs);
r.rhs = clone(r.rhs);
r.lhs = clone(r.lhs)
r.rhs = clone(r.rhs)
case Block_Stmt:
r.label = auto_cast clone(r.label);
r.stmts = clone(r.stmts);
r.label = auto_cast clone(r.label)
r.stmts = clone(r.stmts)
case If_Stmt:
r.label = auto_cast clone(r.label);
r.init = clone(r.init);
r.cond = clone(r.cond);
r.body = clone(r.body);
r.else_stmt = clone(r.else_stmt);
r.label = auto_cast clone(r.label)
r.init = clone(r.init)
r.cond = clone(r.cond)
r.body = clone(r.body)
r.else_stmt = clone(r.else_stmt)
case When_Stmt:
r.cond = clone(r.cond);
r.body = clone(r.body);
r.else_stmt = clone(r.else_stmt);
r.cond = clone(r.cond)
r.body = clone(r.body)
r.else_stmt = clone(r.else_stmt)
case Return_Stmt:
r.results = clone(r.results);
r.results = clone(r.results)
case Defer_Stmt:
r.stmt = clone(r.stmt);
r.stmt = clone(r.stmt)
case For_Stmt:
r.label = auto_cast clone(r.label);
r.init = clone(r.init);
r.cond = clone(r.cond);
r.post = clone(r.post);
r.body = clone(r.body);
r.label = auto_cast clone(r.label)
r.init = clone(r.init)
r.cond = clone(r.cond)
r.post = clone(r.post)
r.body = clone(r.body)
case Range_Stmt:
r.label = auto_cast clone(r.label);
r.vals = clone(r.vals);
r.expr = clone(r.expr);
r.body = clone(r.body);
r.label = auto_cast clone(r.label)
r.vals = clone(r.vals)
r.expr = clone(r.expr)
r.body = clone(r.body)
case Case_Clause:
r.list = clone(r.list);
r.body = clone(r.body);
r.list = clone(r.list)
r.body = clone(r.body)
case Switch_Stmt:
r.label = auto_cast clone(r.label);
r.init = clone(r.init);
r.cond = clone(r.cond);
r.body = clone(r.body);
r.label = auto_cast clone(r.label)
r.init = clone(r.init)
r.cond = clone(r.cond)
r.body = clone(r.body)
case Type_Switch_Stmt:
r.label = auto_cast clone(r.label);
r.tag = clone(r.tag);
r.expr = clone(r.expr);
r.body = clone(r.body);
r.label = auto_cast clone(r.label)
r.tag = clone(r.tag)
r.expr = clone(r.expr)
r.body = clone(r.body)
case Branch_Stmt:
r.label = auto_cast clone(r.label);
r.label = auto_cast clone(r.label)
case Using_Stmt:
r.list = clone(r.list);
r.list = clone(r.list)
case Bad_Decl:
case Value_Decl:
r.attributes = clone(r.attributes);
r.names = clone(r.names);
r.type = clone(r.type);
r.values = clone(r.values);
r.attributes = clone(r.attributes)
r.names = clone(r.names)
r.type = clone(r.type)
r.values = clone(r.values)
case Package_Decl:
case Import_Decl:
case Foreign_Block_Decl:
r.attributes = clone(r.attributes);
r.foreign_library = clone(r.foreign_library);
r.body = clone(r.body);
r.attributes = clone(r.attributes)
r.foreign_library = clone(r.foreign_library)
r.body = clone(r.body)
case Foreign_Import_Decl:
r.name = auto_cast clone(r.name);
r.name = auto_cast clone(r.name)
case Proc_Group:
r.args = clone(r.args);
r.args = clone(r.args)
case Attribute:
r.elems = clone(r.elems);
r.elems = clone(r.elems)
case Field:
r.names = clone(r.names);
r.type = clone(r.type);
r.default_value = clone(r.default_value);
r.names = clone(r.names)
r.type = clone(r.type)
r.default_value = clone(r.default_value)
case Field_List:
r.list = clone(r.list);
r.list = clone(r.list)
case Typeid_Type:
r.specialization = clone(r.specialization);
r.specialization = clone(r.specialization)
case Helper_Type:
r.type = clone(r.type);
r.type = clone(r.type)
case Distinct_Type:
r.type = clone(r.type);
r.type = clone(r.type)
case Poly_Type:
r.type = auto_cast clone(r.type);
r.specialization = clone(r.specialization);
r.type = auto_cast clone(r.type)
r.specialization = clone(r.specialization)
case Proc_Type:
r.params = auto_cast clone(r.params);
r.results = auto_cast clone(r.results);
r.params = auto_cast clone(r.params)
r.results = auto_cast clone(r.results)
case Pointer_Type:
r.elem = clone(r.elem);
r.elem = clone(r.elem)
case Multi_Pointer_Type:
r.elem = clone(r.elem);
r.elem = clone(r.elem)
case Array_Type:
r.len = clone(r.len);
r.elem = clone(r.elem);
r.len = clone(r.len)
r.elem = clone(r.elem)
case Dynamic_Array_Type:
r.elem = clone(r.elem);
r.elem = clone(r.elem)
case Struct_Type:
r.poly_params = auto_cast clone(r.poly_params);
r.align = clone(r.align);
r.fields = auto_cast clone(r.fields);
r.poly_params = auto_cast clone(r.poly_params)
r.align = clone(r.align)
r.fields = auto_cast clone(r.fields)
case Union_Type:
r.poly_params = auto_cast clone(r.poly_params);
r.align = clone(r.align);
r.variants = clone(r.variants);
r.poly_params = auto_cast clone(r.poly_params)
r.align = clone(r.align)
r.variants = clone(r.variants)
case Enum_Type:
r.base_type = clone(r.base_type);
r.fields = clone(r.fields);
r.base_type = clone(r.base_type)
r.fields = clone(r.fields)
case Bit_Set_Type:
r.elem = clone(r.elem);
r.underlying = clone(r.underlying);
r.elem = clone(r.elem)
r.underlying = clone(r.underlying)
case Map_Type:
r.key = clone(r.key);
r.value = clone(r.value);
r.key = clone(r.key)
r.value = clone(r.value)
case:
fmt.panicf("Unhandled node kind: %T", r);
fmt.panicf("Unhandled node kind: %T", r)
}
return res;
return res
}
+157 -157
View File
@@ -18,15 +18,15 @@ Visitor :: struct {
inspect :: proc(node: ^Node, f: proc(^Node) -> bool) {
v := &Visitor{
visit = proc(v: ^Visitor, node: ^Node) -> ^Visitor {
f := (proc(^Node) -> bool)(v.data);
f := (proc(^Node) -> bool)(v.data)
if f(node) {
return v;
return v
}
return nil;
return nil
},
data = rawptr(f),
};
walk(v, node);
}
walk(v, node)
}
@@ -37,36 +37,36 @@ inspect :: proc(node: ^Node, f: proc(^Node) -> bool) {
walk :: proc(v: ^Visitor, node: ^Node) {
walk_expr_list :: proc(v: ^Visitor, list: []^Expr) {
for x in list {
walk(v, x);
walk(v, x)
}
}
walk_stmt_list :: proc(v: ^Visitor, list: []^Stmt) {
for x in list {
walk(v, x);
walk(v, x)
}
}
walk_attribute_list :: proc(v: ^Visitor, list: []^Attribute) {
for x in list {
walk(v, x);
walk(v, x)
}
}
v := v;
v := v
if v = v->visit(node); v == nil {
return;
return
}
switch n in &node.derived {
case File:
if n.docs != nil {
walk(v, n.docs);
walk(v, n.docs)
}
walk_stmt_list(v, n.decls[:]);
walk_stmt_list(v, n.decls[:])
case Package:
for _, f in n.files {
walk(v, f);
walk(v, f)
}
case Comment_Group:
@@ -79,330 +79,330 @@ walk :: proc(v: ^Visitor, node: ^Node) {
case Basic_Directive:
case Ellipsis:
if n.expr != nil {
walk(v, n.expr);
walk(v, n.expr)
}
case Proc_Lit:
walk(v, n.type);
walk(v, n.body);
walk_expr_list(v, n.where_clauses);
walk(v, n.type)
walk(v, n.body)
walk_expr_list(v, n.where_clauses)
case Comp_Lit:
if n.type != nil {
walk(v, n.type);
walk(v, n.type)
}
walk_expr_list(v, n.elems);
walk_expr_list(v, n.elems)
case Tag_Expr:
walk(v, n.expr);
walk(v, n.expr)
case Unary_Expr:
walk(v, n.expr);
walk(v, n.expr)
case Binary_Expr:
walk(v, n.left);
walk(v, n.right);
walk(v, n.left)
walk(v, n.right)
case Paren_Expr:
walk(v, n.expr);
walk(v, n.expr)
case Selector_Expr:
walk(v, n.expr);
walk(v, n.field);
walk(v, n.expr)
walk(v, n.field)
case Implicit_Selector_Expr:
walk(v, n.field);
walk(v, n.field)
case Selector_Call_Expr:
walk(v, n.expr);
walk(v, n.call);
walk(v, n.expr)
walk(v, n.call)
case Index_Expr:
walk(v, n.expr);
walk(v, n.index);
walk(v, n.expr)
walk(v, n.index)
case Deref_Expr:
walk(v, n.expr);
walk(v, n.expr)
case Slice_Expr:
walk(v, n.expr);
walk(v, n.expr)
if n.low != nil {
walk(v, n.low);
walk(v, n.low)
}
if n.high != nil {
walk(v, n.high);
walk(v, n.high)
}
case Call_Expr:
walk(v, n.expr);
walk_expr_list(v, n.args);
walk(v, n.expr)
walk_expr_list(v, n.args)
case Field_Value:
walk(v, n.field);
walk(v, n.value);
walk(v, n.field)
walk(v, n.value)
case Ternary_If_Expr:
walk(v, n.x);
walk(v, n.cond);
walk(v, n.y);
walk(v, n.x)
walk(v, n.cond)
walk(v, n.y)
case Ternary_When_Expr:
walk(v, n.x);
walk(v, n.cond);
walk(v, n.y);
walk(v, n.x)
walk(v, n.cond)
walk(v, n.y)
case Or_Else_Expr:
walk(v, n.x);
walk(v, n.y);
walk(v, n.x)
walk(v, n.y)
case Or_Return_Expr:
walk(v, n.expr);
walk(v, n.expr)
case Type_Assertion:
walk(v, n.expr);
walk(v, n.expr)
if n.type != nil {
walk(v, n.type);
walk(v, n.type)
}
case Type_Cast:
walk(v, n.type);
walk(v, n.expr);
walk(v, n.type)
walk(v, n.expr)
case Auto_Cast:
walk(v, n.expr);
walk(v, n.expr)
case Inline_Asm_Expr:
walk_expr_list(v, n.param_types);
walk(v, n.return_type);
walk(v, n.constraints_string);
walk(v, n.asm_string);
walk_expr_list(v, n.param_types)
walk(v, n.return_type)
walk(v, n.constraints_string)
walk(v, n.asm_string)
case Bad_Stmt:
case Empty_Stmt:
case Expr_Stmt:
walk(v, n.expr);
walk(v, n.expr)
case Tag_Stmt:
walk(v, n.stmt);
walk(v, n.stmt)
case Assign_Stmt:
walk_expr_list(v, n.lhs);
walk_expr_list(v, n.rhs);
walk_expr_list(v, n.lhs)
walk_expr_list(v, n.rhs)
case Block_Stmt:
if n.label != nil {
walk(v, n.label);
walk(v, n.label)
}
walk_stmt_list(v, n.stmts);
walk_stmt_list(v, n.stmts)
case If_Stmt:
if n.label != nil {
walk(v, n.label);
walk(v, n.label)
}
if n.init != nil {
walk(v, n.init);
walk(v, n.init)
}
walk(v, n.cond);
walk(v, n.body);
walk(v, n.cond)
walk(v, n.body)
if n.else_stmt != nil {
walk(v, n.else_stmt);
walk(v, n.else_stmt)
}
case When_Stmt:
walk(v, n.cond);
walk(v, n.body);
walk(v, n.cond)
walk(v, n.body)
if n.else_stmt != nil {
walk(v, n.else_stmt);
walk(v, n.else_stmt)
}
case Return_Stmt:
walk_expr_list(v, n.results);
walk_expr_list(v, n.results)
case Defer_Stmt:
walk(v, n.stmt);
walk(v, n.stmt)
case For_Stmt:
if n.label != nil {
walk(v, n.label);
walk(v, n.label)
}
if n.init != nil {
walk(v, n.init);
walk(v, n.init)
}
if n.cond != nil {
walk(v, n.cond);
walk(v, n.cond)
}
if n.post != nil {
walk(v, n.post);
walk(v, n.post)
}
walk(v, n.body);
walk(v, n.body)
case Range_Stmt:
if n.label != nil {
walk(v, n.label);
walk(v, n.label)
}
for val in n.vals {
if val != nil {
walk(v, val);
walk(v, val)
}
}
walk(v, n.expr);
walk(v, n.body);
walk(v, n.expr)
walk(v, n.body)
case Inline_Range_Stmt:
if n.label != nil {
walk(v, n.label);
walk(v, n.label)
}
if n.val0 != nil {
walk(v, n.val0);
walk(v, n.val0)
}
if n.val1 != nil {
walk(v, n.val1);
walk(v, n.val1)
}
walk(v, n.expr);
walk(v, n.body);
walk(v, n.expr)
walk(v, n.body)
case Case_Clause:
walk_expr_list(v, n.list);
walk_stmt_list(v, n.body);
walk_expr_list(v, n.list)
walk_stmt_list(v, n.body)
case Switch_Stmt:
if n.label != nil {
walk(v, n.label);
walk(v, n.label)
}
if n.init != nil {
walk(v, n.init);
walk(v, n.init)
}
if n.cond != nil {
walk(v, n.cond);
walk(v, n.cond)
}
walk(v, n.body);
walk(v, n.body)
case Type_Switch_Stmt:
if n.label != nil {
walk(v, n.label);
walk(v, n.label)
}
if n.tag != nil {
walk(v, n.tag);
walk(v, n.tag)
}
if n.expr != nil {
walk(v, n.expr);
walk(v, n.expr)
}
walk(v, n.body);
walk(v, n.body)
case Branch_Stmt:
if n.label != nil {
walk(v, n.label);
walk(v, n.label)
}
case Using_Stmt:
walk_expr_list(v, n.list);
walk_expr_list(v, n.list)
case Bad_Decl:
case Value_Decl:
if n.docs != nil {
walk(v, n.docs);
walk(v, n.docs)
}
walk_attribute_list(v, n.attributes[:]);
walk_expr_list(v, n.names);
walk_attribute_list(v, n.attributes[:])
walk_expr_list(v, n.names)
if n.type != nil {
walk(v, n.type);
walk(v, n.type)
}
walk_expr_list(v, n.values);
walk_expr_list(v, n.values)
if n.comment != nil {
walk(v, n.comment);
walk(v, n.comment)
}
case Package_Decl:
if n.docs != nil {
walk(v, n.docs);
walk(v, n.docs)
}
if n.comment != nil {
walk(v, n.comment);
walk(v, n.comment)
}
case Import_Decl:
if n.docs != nil {
walk(v, n.docs);
walk(v, n.docs)
}
if n.comment != nil {
walk(v, n.comment);
walk(v, n.comment)
}
case Foreign_Block_Decl:
if n.docs != nil {
walk(v, n.docs);
walk(v, n.docs)
}
walk_attribute_list(v, n.attributes[:]);
walk_attribute_list(v, n.attributes[:])
if n.foreign_library != nil {
walk(v, n.foreign_library);
walk(v, n.foreign_library)
}
walk(v, n.body);
walk(v, n.body)
case Foreign_Import_Decl:
if n.docs != nil {
walk(v, n.docs);
walk(v, n.docs)
}
walk_attribute_list(v, n.attributes[:]);
walk(v, n.name);
walk_attribute_list(v, n.attributes[:])
walk(v, n.name)
if n.comment != nil {
walk(v, n.comment);
walk(v, n.comment)
}
case Proc_Group:
walk_expr_list(v, n.args);
walk_expr_list(v, n.args)
case Attribute:
walk_expr_list(v, n.elems);
walk_expr_list(v, n.elems)
case Field:
if n.docs != nil {
walk(v, n.docs);
walk(v, n.docs)
}
walk_expr_list(v, n.names);
walk_expr_list(v, n.names)
if n.type != nil {
walk(v, n.type);
walk(v, n.type)
}
if n.default_value != nil {
walk(v, n.default_value);
walk(v, n.default_value)
}
if n.comment != nil {
walk(v, n.comment);
walk(v, n.comment)
}
case Field_List:
for x in n.list {
walk(v, x);
walk(v, x)
}
case Typeid_Type:
if n.specialization != nil {
walk(v, n.specialization);
walk(v, n.specialization)
}
case Helper_Type:
walk(v, n.type);
walk(v, n.type)
case Distinct_Type:
walk(v, n.type);
walk(v, n.type)
case Poly_Type:
walk(v, n.type);
walk(v, n.type)
if n.specialization != nil {
walk(v, n.specialization);
walk(v, n.specialization)
}
case Proc_Type:
walk(v, n.params);
walk(v, n.results);
walk(v, n.params)
walk(v, n.results)
case Pointer_Type:
walk(v, n.elem);
walk(v, n.elem)
case Multi_Pointer_Type:
walk(v, n.elem);
walk(v, n.elem)
case Array_Type:
if n.tag != nil {
walk(v, n.tag);
walk(v, n.tag)
}
if n.len != nil {
walk(v, n.len);
walk(v, n.len)
}
walk(v, n.elem);
walk(v, n.elem)
case Dynamic_Array_Type:
if n.tag != nil {
walk(v, n.tag);
walk(v, n.tag)
}
walk(v, n.elem);
walk(v, n.elem)
case Struct_Type:
if n.poly_params != nil {
walk(v, n.poly_params);
walk(v, n.poly_params)
}
if n.align != nil {
walk(v, n.align);
walk(v, n.align)
}
walk_expr_list(v, n.where_clauses);
walk(v, n.fields);
walk_expr_list(v, n.where_clauses)
walk(v, n.fields)
case Union_Type:
if n.poly_params != nil {
walk(v, n.poly_params);
walk(v, n.poly_params)
}
if n.align != nil {
walk(v, n.align);
walk(v, n.align)
}
walk_expr_list(v, n.where_clauses);
walk_expr_list(v, n.variants);
walk_expr_list(v, n.where_clauses)
walk_expr_list(v, n.variants)
case Enum_Type:
if n.base_type != nil {
walk(v, n.base_type);
walk(v, n.base_type)
}
walk_expr_list(v, n.fields);
walk_expr_list(v, n.fields)
case Bit_Set_Type:
walk(v, n.elem);
walk(v, n.elem)
if n.underlying != nil {
walk(v, n.underlying);
walk(v, n.underlying)
}
case Map_Type:
walk(v, n.key);
walk(v, n.value);
walk(v, n.key)
walk(v, n.value)
case Relative_Type:
walk(v, n.tag);
walk(v, n.type);
walk(v, n.tag)
walk(v, n.type)
case:
fmt.panicf("ast.walk: unexpected node type %T", n);
fmt.panicf("ast.walk: unexpected node type %T", n)
}
v->visit(nil);
v->visit(nil)
}
+36 -36
View File
@@ -7,24 +7,24 @@ Array :: struct($T: typeid) {
length: u32le,
}
String :: distinct Array(byte);
String :: distinct Array(byte)
Version_Type_Major :: 0;
Version_Type_Minor :: 1;
Version_Type_Patch :: 0;
Version_Type_Major :: 0
Version_Type_Minor :: 1
Version_Type_Patch :: 0
Version_Type :: struct {
major, minor, patch: u8,
_: u8,
};
}
Version_Type_Default :: Version_Type{
major=Version_Type_Major,
minor=Version_Type_Minor,
patch=Version_Type_Patch,
};
}
Magic_String :: "odindoc\x00";
Magic_String :: "odindoc\x00"
Header_Base :: struct {
magic: [8]byte,
@@ -45,10 +45,10 @@ Header :: struct {
types: Array(Type),
}
File_Index :: distinct u32le;
Pkg_Index :: distinct u32le;
Entity_Index :: distinct u32le;
Type_Index :: distinct u32le;
File_Index :: distinct u32le
Pkg_Index :: distinct u32le
Entity_Index :: distinct u32le
Type_Index :: distinct u32le
Position :: struct {
@@ -56,7 +56,7 @@ Position :: struct {
line: u32le,
column: u32le,
offset: u32le,
};
}
File :: struct {
pkg: Pkg_Index,
@@ -69,7 +69,7 @@ Pkg_Flag :: enum u32le {
Init = 2,
}
Pkg_Flags :: distinct bit_set[Pkg_Flag; u32le];
Pkg_Flags :: distinct bit_set[Pkg_Flag; u32le]
Pkg :: struct {
fullpath: String,
@@ -108,7 +108,7 @@ Entity_Flag :: enum u32le {
Var_Static = 10,
}
Entity_Flags :: distinct bit_set[Entity_Flag; u32le];
Entity_Flags :: distinct bit_set[Entity_Flag; u32le]
Entity :: struct {
kind: Entity_Kind,
@@ -169,7 +169,7 @@ Type_Kind :: enum u32le {
Multi_Pointer = 22,
}
Type_Elems_Cap :: 4;
Type_Elems_Cap :: 4
Type :: struct {
kind: Type_Kind,
@@ -239,26 +239,26 @@ Type :: struct {
where_clauses: Array(String),
}
Type_Flags_Basic :: distinct bit_set[Type_Flag_Basic; u32le];
Type_Flags_Basic :: distinct bit_set[Type_Flag_Basic; u32le]
Type_Flag_Basic :: enum u32le {
Untyped = 1,
}
Type_Flags_Struct :: distinct bit_set[Type_Flag_Struct; u32le];
Type_Flags_Struct :: distinct bit_set[Type_Flag_Struct; u32le]
Type_Flag_Struct :: enum u32le {
Polymorphic = 0,
Packed = 1,
Raw_Union = 2,
}
Type_Flags_Union :: distinct bit_set[Type_Flag_Union; u32le];
Type_Flags_Union :: distinct bit_set[Type_Flag_Union; u32le]
Type_Flag_Union :: enum u32le {
Polymorphic = 0,
No_Nil = 1,
Maybe = 2,
}
Type_Flags_Proc :: distinct bit_set[Type_Flag_Proc; u32le];
Type_Flags_Proc :: distinct bit_set[Type_Flag_Proc; u32le]
Type_Flag_Proc :: enum u32le {
Polymorphic = 0,
Diverging = 1,
@@ -267,7 +267,7 @@ Type_Flag_Proc :: enum u32le {
C_Vararg = 4,
}
Type_Flags_Bit_Set :: distinct bit_set[Type_Flag_Bit_Set; u32le];
Type_Flags_Bit_Set :: distinct bit_set[Type_Flag_Bit_Set; u32le]
Type_Flag_Bit_Set :: enum u32le {
Range = 1,
Op_Lt = 2,
@@ -276,13 +276,13 @@ Type_Flag_Bit_Set :: enum u32le {
}
from_array :: proc(base: ^Header_Base, a: $A/Array($T)) -> []T {
s: mem.Raw_Slice;
s.data = rawptr(uintptr(base) + uintptr(a.offset));
s.len = int(a.length);
return transmute([]T)s;
s: mem.Raw_Slice
s.data = rawptr(uintptr(base) + uintptr(a.offset))
s.len = int(a.length)
return transmute([]T)s
}
from_string :: proc(base: ^Header_Base, s: String) -> string {
return string(from_array(base, s));
return string(from_array(base, s))
}
@@ -298,22 +298,22 @@ Reader_Error :: enum {
read_from_bytes :: proc(data: []byte) -> (h: ^Header, err: Reader_Error) {
if len(data) < size_of(Header_Base) {
err = .Header_Too_Small;
return;
err = .Header_Too_Small
return
}
header_base := (^Header_Base)(raw_data(data));
header_base := (^Header_Base)(raw_data(data))
if header_base.magic != Magic_String {
err = .Invalid_Magic;
return;
err = .Invalid_Magic
return
}
if len(data) < int(header_base.total_size) {
err = .Data_Too_Small;
return;
err = .Data_Too_Small
return
}
if header_base.version != Version_Type_Default {
err = .Invalid_Version;
return;
err = .Invalid_Version
return
}
h = (^Header)(header_base);
return;
h = (^Header)(header_base)
return
}
+17 -17
View File
@@ -4,38 +4,38 @@ import "core:odin/printer"
import "core:odin/parser"
import "core:odin/ast"
default_style := printer.default_style;
default_style := printer.default_style
simplify :: proc(file: ^ast.File) {
}
format :: proc(filepath: string, source: string, config: printer.Config, parser_flags := parser.Flags{}, allocator := context.allocator) -> (string, bool) {
config := config;
config := config
pkg := ast.Package {
kind = .Normal,
};
}
file := ast.File {
pkg = &pkg,
src = source,
fullpath = filepath,
};
config.newline_limit = clamp(config.newline_limit, 0, 16);
config.spaces = clamp(config.spaces, 1, 16);
config.align_length_break = clamp(config.align_length_break, 0, 64);
p := parser.default_parser(parser_flags);
ok := parser.parse_file(&p, &file);
if !ok || file.syntax_error_count > 0 {
return {}, false;
}
prnt := printer.make_printer(config, allocator);
config.newline_limit = clamp(config.newline_limit, 0, 16)
config.spaces = clamp(config.spaces, 1, 16)
config.align_length_break = clamp(config.align_length_break, 0, 64)
return printer.print(&prnt, &file), true;
p := parser.default_parser(parser_flags)
ok := parser.parse_file(&p, &file)
if !ok || file.syntax_error_count > 0 {
return {}, false
}
prnt := printer.make_printer(config, allocator)
return printer.print(&prnt, &file), true
}
+39 -39
View File
@@ -8,82 +8,82 @@ import "core:os"
import "core:slice"
collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
NO_POS :: tokenizer.Pos{};
NO_POS :: tokenizer.Pos{}
pkg_path, pkg_path_ok := filepath.abs(path);
pkg_path, pkg_path_ok := filepath.abs(path)
if !pkg_path_ok {
return;
return
}
path_pattern := fmt.tprintf("%s/*.odin", pkg_path);
matches, err := filepath.glob(path_pattern);
defer delete(matches);
path_pattern := fmt.tprintf("%s/*.odin", pkg_path)
matches, err := filepath.glob(path_pattern)
defer delete(matches)
if err != nil {
return;
return
}
pkg = ast.new(ast.Package, NO_POS, NO_POS);
pkg.fullpath = pkg_path;
pkg = ast.new(ast.Package, NO_POS, NO_POS)
pkg.fullpath = pkg_path
for match in matches {
src: []byte;
fullpath, ok := filepath.abs(match);
src: []byte
fullpath, ok := filepath.abs(match)
if !ok {
return;
return
}
src, ok = os.read_entire_file(fullpath);
src, ok = os.read_entire_file(fullpath)
if !ok {
delete(fullpath);
return;
delete(fullpath)
return
}
file := ast.new(ast.File, NO_POS, NO_POS);
file.pkg = pkg;
file.src = string(src);
file.fullpath = fullpath;
pkg.files[fullpath] = file;
file := ast.new(ast.File, NO_POS, NO_POS)
file.pkg = pkg
file.src = string(src)
file.fullpath = fullpath
pkg.files[fullpath] = file
}
success = true;
return;
success = true
return
}
parse_package :: proc(pkg: ^ast.Package, p: ^Parser = nil) -> bool {
p := p;
p := p
if p == nil {
p = &Parser{};
p^ = default_parser();
p = &Parser{}
p^ = default_parser()
}
ok := true;
ok := true
files := make([]^ast.File, len(pkg.files), context.temp_allocator);
i := 0;
files := make([]^ast.File, len(pkg.files), context.temp_allocator)
i := 0
for _, file in pkg.files {
files[i] = file;
i += 1;
files[i] = file
i += 1
}
slice.sort(files);
slice.sort(files)
for file in files {
if !parse_file(p, file) {
ok = false;
ok = false
}
if pkg.name == "" {
pkg.name = file.pkg_decl.name;
pkg.name = file.pkg_decl.name
} else if pkg.name != file.pkg_decl.name {
error(p, file.pkg_decl.pos, "different package name, expected '%s', got '%s'", pkg.name, file.pkg_decl.name);
error(p, file.pkg_decl.pos, "different package name, expected '%s', got '%s'", pkg.name, file.pkg_decl.name)
}
}
return ok;
return ok
}
parse_package_from_path :: proc(path: string, p: ^Parser = nil) -> (pkg: ^ast.Package, ok: bool) {
pkg, ok = collect_package(path);
pkg, ok = collect_package(path)
if !ok {
return;
return
}
ok = parse_package(pkg, p);
return;
ok = parse_package(pkg, p)
return
}
+1664 -1664
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+23 -23
View File
@@ -17,15 +17,15 @@ Pos :: struct {
pos_compare :: proc(lhs, rhs: Pos) -> int {
if lhs.offset != rhs.offset {
return -1 if (lhs.offset < rhs.offset) else +1;
return -1 if (lhs.offset < rhs.offset) else +1
}
if lhs.line != rhs.line {
return -1 if (lhs.line < rhs.line) else +1;
return -1 if (lhs.line < rhs.line) else +1
}
if lhs.column != rhs.column {
return -1 if (lhs.column < rhs.column) else +1;
return -1 if (lhs.column < rhs.column) else +1
}
return strings.compare(lhs.file, rhs.file);
return strings.compare(lhs.file, rhs.file)
}
Token_Kind :: enum u32 {
@@ -156,7 +156,7 @@ Token_Kind :: enum u32 {
B_Custom_Keyword_Begin = COUNT+1,
// ... Custom keywords
};
}
tokens := [Token_Kind.COUNT]string {
"Invalid",
@@ -281,60 +281,60 @@ tokens := [Token_Kind.COUNT]string {
"inline",
"no_inline",
"",
};
}
custom_keyword_tokens: []string;
custom_keyword_tokens: []string
is_newline :: proc(tok: Token) -> bool {
return tok.kind == .Semicolon && tok.text == "\n";
return tok.kind == .Semicolon && tok.text == "\n"
}
token_to_string :: proc(tok: Token) -> string {
if is_newline(tok) {
return "newline";
return "newline"
}
return to_string(tok.kind);
return to_string(tok.kind)
}
to_string :: proc(kind: Token_Kind) -> string {
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
return tokens[kind];
return tokens[kind]
}
if Token_Kind.B_Custom_Keyword_Begin < kind {
n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin));
n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin))
if n < len(custom_keyword_tokens) {
return custom_keyword_tokens[n];
return custom_keyword_tokens[n]
}
}
return "Invalid";
return "Invalid"
}
is_literal :: proc(kind: Token_Kind) -> bool {
return Token_Kind.B_Literal_Begin < kind && kind < Token_Kind.B_Literal_End;
return Token_Kind.B_Literal_Begin < kind && kind < Token_Kind.B_Literal_End
}
is_operator :: proc(kind: Token_Kind) -> bool {
#partial switch kind {
case .B_Operator_Begin .. .B_Operator_End:
return true;
return true
case .In, .Not_In:
return true;
return true
case .If:
return true;
return true
}
return false;
return false
}
is_assignment_operator :: proc(kind: Token_Kind) -> bool {
return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq;
return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq
}
is_keyword :: proc(kind: Token_Kind) -> bool {
switch {
case Token_Kind.B_Keyword_Begin < kind && kind < Token_Kind.B_Keyword_End:
return true;
return true
case Token_Kind.B_Custom_Keyword_Begin < kind:
return true;
return true
}
return false;
return false
}
File diff suppressed because it is too large Load Diff
+48 -48
View File
@@ -8,100 +8,100 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
// Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
return;
return
}
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
return;
return
}
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])});
fi.fullpath = path;
fi.name = basename(path);
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])})
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
fi.mode |= 0o444;
fi.mode |= 0o444
} else {
fi.mode |= 0o666;
fi.mode |= 0o666
}
is_sym := false;
is_sym := false
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 {
is_sym = false;
is_sym = false
} else {
is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT;
is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT
}
if is_sym {
fi.mode |= File_Mode_Sym_Link;
fi.mode |= File_Mode_Sym_Link
} else {
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
fi.mode |= 0o111 | File_Mode_Dir;
fi.mode |= 0o111 | File_Mode_Dir
}
// fi.mode |= file_type_mode(h);
}
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime));
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime));
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime));
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
fi.is_dir = fi.mode & File_Mode_Dir != 0;
return;
fi.is_dir = fi.mode & File_Mode_Dir != 0
return
}
if fd == 0 {
return nil, ERROR_INVALID_HANDLE;
return nil, ERROR_INVALID_HANDLE
}
context.allocator = allocator;
context.allocator = allocator
h := win32.HANDLE(fd);
h := win32.HANDLE(fd)
dir_fi, _ := file_info_from_get_file_information_by_handle("", h);
dir_fi, _ := file_info_from_get_file_information_by_handle("", h)
if !dir_fi.is_dir {
return nil, ERROR_FILE_IS_NOT_DIR;
return nil, ERROR_FILE_IS_NOT_DIR
}
n := n;
size := n;
n := n
size := n
if n <= 0 {
n = -1;
size = 100;
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size);
dfi := make([dynamic]File_Info, 0, size)
wpath: []u16;
wpath, err = cleanpath_from_handle_u16(fd);
wpath: []u16
wpath, err = cleanpath_from_handle_u16(fd)
if len(wpath) == 0 || err != ERROR_NONE {
return;
return
}
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator);
copy(wpath_search, wpath);
wpath_search[len(wpath)+0] = '\\';
wpath_search[len(wpath)+1] = '*';
wpath_search[len(wpath)+2] = 0;
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator)
copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\'
wpath_search[len(wpath)+1] = '*'
wpath_search[len(wpath)+2] = 0
path := cleanpath_from_buf(wpath);
path := cleanpath_from_buf(wpath)
find_data := &win32.WIN32_FIND_DATAW{};
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data);
defer win32.FindClose(find_handle);
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
defer win32.FindClose(find_handle)
for n != 0 && find_handle != nil {
fi: File_Info;
fi = find_data_to_file_info(path, find_data);
fi: File_Info
fi = find_data_to_file_info(path, find_data)
if fi.name != "" {
append(&dfi, fi);
n -= 1;
append(&dfi, fi)
n -= 1
}
if !win32.FindNextFileW(find_handle, find_data) {
e := Errno(win32.GetLastError());
e := Errno(win32.GetLastError())
if e == ERROR_NO_MORE_FILES {
break;
break
}
return dfi[:], e;
return dfi[:], e
}
}
return dfi[:], ERROR_NONE;
return dfi[:], ERROR_NONE
}

Some files were not shown because too many files have changed in this diff Show More