mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-03 10:31:48 -07:00
Merge pull request #1112 from odin-lang/optional-semicolons
Optional Semicolons
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+79
-79
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,3 +1,3 @@
|
||||
package dynlib
|
||||
|
||||
Library :: distinct rawptr;
|
||||
Library :: distinct rawptr
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+78
-78
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+870
-870
File diff suppressed because it is too large
Load Diff
+46
-46
@@ -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
File diff suppressed because it is too large
Load Diff
+933
-933
File diff suppressed because it is too large
Load Diff
+302
-302
File diff suppressed because it is too large
Load Diff
+193
-193
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) }
|
||||
|
||||
+1461
-1461
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+16
-16
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+105
-105
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+259
-259
File diff suppressed because it is too large
Load Diff
+619
-619
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
|
||||
+312
-312
File diff suppressed because it is too large
Load Diff
+48
-48
@@ -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
Reference in New Issue
Block a user